跳转到内容

数及其运算

自然数/整数/有理数/实数及其运算

【略】

进制与进制转换

进制的表示

进制就是进位计数制,是一种带进位的计数方法。

我们平时生活当中所使用的数字系统是十进制的,而计算机内部是使用二进制来表示的,除此之外,还有一些其它的进制,比如时间中的时、分、秒,用的是六十进制,在 C++ 中,变量的内存地址是用十六进制表示的(如:0x7ffde284d5a4,其中 0x 代表十六进制),网页里的颜色表示很多时候用的也是十六进制(如:#FF0000 代表红色)。

下面是常见的四种进制及其表示。

进制基数进位条件组成数字/字符
二进制(B)2逢 2 进 10 和 1,共 2 个数字
八进制(O)8逢 8 进 10 ~ 7,共 8 个数字
十进制(D)10逢 10 进 10 ~ 9,共 10 个数字
十六进制(H)16逢 16 进 10 ~ 9,A ~ F,共 16 个字符

根据需求,不同进制之间是可以相互转换的,大致可分为下面两大类:

  1. 十进制与其它 nn 进制之间的相互转换
  2. 二、八、十六进制之间的相互转换

下面我们一起来看看。

十进制转 kk 进制

这里以十进制转二进制为例,可以将十进制数划分为整数部分和小数部分(若没有小数部分则省略)。整数部分采用除二取余法,小数部分采用乘二取整法。

例如,要将二进制数 26.3125 转换为二进制数,分别用除二取余法和乘二取整数转换整数和小数部分。

除二取余法

就是用 2 连续去除十进制整数,直到商为 0 为止,最后将余数逆序输出即可得到该十进制数所对应的二进制数表示。

为了方便,用 (26)10(26)_{10} 来表示该数是十进制表示,(10101)2(10101)_{2} 表示的该数为二进制表示。

例如,下图是用除二取余法将整数部分 26 转换为二进制的过程。

因此整数部分十进制 (26)10(26)_{10} 的二进制表示为 (11010)2(11010)_{2}

同样道理,如果要将十进制转换为其它的 kk 进制,只需要除 kk 取余,直到商为 0,逆序输出余数即可。

乘二取整法

用 2 连续去乘十进制数,将得到的积的整数取出,继续用 2 去乘余下的小数部分,如此循环,直到得到的积的小数部分为 0 为止,最后将每次取出的整数部分按顺序排列并输出,就可以得到小数的二进制表示。

有的小数在用乘二取整法转换时,会无限运算下去,所以根据需要的精度取足够的位数即可停止计算。

下图是用乘二取整法将小数部分 0.3125 转换为二进制的过程。

同样道理,如果要将十进制转换为其它的 kk 进制,只需要乘 kk 取整,直到积的小数部分为 0,按顺序序输出每次的整数部分即可。

需要注意的是,很多浮点数都无法用二进制来精确表示,比如说 0.1,正因为如何,才会出现 `0.1+0.20.30.1 + 0.2 \ne 0.3 这个问题出现,甚至有专门的一个 Floating Point Math 网站 来记录在不同语言中这个问题的情况,感兴趣的同学可以去看看。

十进制转二进制代码实现

代码实现有两个关键的部分。

1. 除二取余的实现

这部分与之前学过的十进制数字拆分完全一致,配合应用用 %/ 运算即可实现。

2. 逆序输出的实现

有两种方法实现逆序输出。

  • 数组方法:将每次得到的余数存入一个数组,运算完成后,将数组逆序输出。
  • 字符串拼接方法:利用字符拼接的先后顺序来实现逆序输出。

通过数组方法逆序输出,代码如下:

#include <iostream>
using namespace std;
const int N = 100; // 最长的二进制位数
int q[N];
int main() {
int n;
cin >> n;
int k = 0; // 用于计位数
while (n > 0) {
q[k] = n % 2;
n = n / 2;
k++;
}
// 逆序输出,得到转换后的二进制数
for (int i = k - 1; i >= 0; i--) {
cout << q[i];
}
return 0;
}

通过字符串拼接输出,代码如下:

#include <iostream>
using namespace std;
int main() {
int n;
cin >> n;
string s = "";
while (n > 0) {
int t = n % 2;
char c = t + '0'; // 将数字转换为对应的字符
s = c + s; // 将字符 c 放在前面拼接,实现最终的倒序
n /= 2;
}
cout << s << endl;
return 0;
}

十进制转十六进制代码实现

十进制转十六进制稍微要麻烦一点,因为十六进制数中还包括对字母 AA ~ BB 的表示和处理。

实现代码如下:

#include <iostream>
using namespace std;
int main() {
int n;
cin >> n;
char c;
string s = "";
while (n > 0) {
int t = n % 16;
//分别处理数字与字母的对应关系
if (t < 10) {
c = t + '0';
} else {
c = t + 'A' - 10;
}
s = c + s; // 利用字符串拼接逆序输出
n /= 16; // 十六进制应该除以 16
}
if (s == "") cout << 0;
else cout << s;
return 0;
}

kk 进制转十进制

一个十进制的数字,anan1...a2a1a0a_n a_{n-1} ... a_2 a_1 a_0 (其中 aia_i 表示数字中的一位),可以表示为:

an×102+an1×10n1+...+a2×102+a1×101+a0×100a_n \times 10^2 + a_{n-1} \times 10^{n-1} + ... + a_2 \times 10^2 + a_1 \times 10^1 + a_0 \times 10^0

举例来说,对于十进制数 (1234)10(1234)_{10} 可以表示为(加权展开):

1×103+2×102+3×101+4×1001 \times 10^3 + 2 \times 10^2 + 3 \times 10^1 + 4 \times 10^0

实际上,一个 kk 进制的数字,遵循同样的规则,可以按加权求和的方式来得到对应的十进制数

例如,对于二进制数 (1101)2(1101)_2 ,可以表示为:

1×23+1×22+0×21+1×20=(13)101 \times 2^3 + 1 \times 2^2 + 0 \times 2^1 + 1 \times 2^0 = (13)_{10}

对于一个 kk 进制数,用数字乘以对应数字位上的位权(不同的进制,位权不同),求和即可得到对应的十进制数。

二进制转十进制代码实现

下面以二进制数转十进制为例,代码实现如下:

// 二进制转十进制,加权求和
#include <iostream>
using namespace std;
int main() {
int n;
cin >> n;
int t = 1, sum = 0;
while (n > 0) {
sum = sum + (n % 10) * t; // 拆开数字,加权求和
n = n / 10;
t = t * 2;
}
cout << sum << endl;
return 0;
}

十六进制转十进制代码实现

因为十六进制数含有字母,在输入时,以字符串来输入,在转换时需要考虑到对应字母的转换。其它的部分与二进制转十进制的逻辑是一致的。

#include <iostream>
using namespace std;
int main() {
string s;
cin >> s;
int t = 1, sum = 0;
for (int i = s.size() - 1; i >= 0; i--) {
if (isdigit(s[i])) {
sum = sum + (s[i] - '0') * t;
} else {
sum = sum + (s[i] - 'A' + 10) * t;
}
t = t * 16;
}
cout << sum << endl;
return 0;
}

其它非十进制互转

小结