整数的表示

本文主要讲解信息在计算机中是怎么表示的。

补码

首先需要知道的前提是计算机中只能进行加法,但是我们需要表示负数。于是有了补码。
计算机中所有的数都是以补码形式存在的,加减运算都是补码之间的加法运算。但是要知道在补码之前,还有原码和反码。
原码就是直接把最高位来区分正负号,这样的确很方便人们阅读,但是不方便计算机进行加法。而且出现了两个 0,分别是 0000 和 1000。
反码是原码到补码之间的过渡形态。反码对负数的原码(除了符号位之外)进行取反。但是这样依然存在两个 0, 分别是 0000 和 1111。但是它比原码的进步的地方是间隔点少了,原码有两个间隔点,分别是从 0111(+7) 到1000(-0) 和从 1111(-7) 到 0000(+0)。而反码只有一个间隔点,就是从 0111(+7) 到 1000(-8)。
补码则是把负数的补码加 1,这样就可以少了个 0,同时最小的负数也减小 1。这样的结果也导致了正数和负数的不对称性。它的优点就是负数可以直接拿来做加法。理解起来就是因为补码在 -1 和 0 这里衔接起来了,形成了模,天道有轮回,所以可以直接加起来。

十进制补码
00000
+10001
+20010
+30011
+40100
+50101
+60110
+70111
-81000
-71001
-61010
-51011
-41100
-31101
-21110
-11111

类型转换

有符号数和无符号数进行转换的时候,数值可能变化,但是位模式不变。也就是说实际的存储不变,只是解释的方式变了。

1
2
3
4
5
6
int main() {
unsigned int u = 4294967295u;
int tu = (int) u;
printf("%d", tu);
return 0;
}

举个例子,这里的 tu 输出是 -1。

如果一个表达式中既有无符号数也有有符号数,那么会隐式转化为无符号数进行比较。

1
2
3
4
5
6
7
8
int main() {
unsigned int u = 0u;
int t = -1;
if (t > u) {
printf("t > u");
}
return 0;
}

在这里 t 是大于 u。也就是 -1 > 0u。

扩展

在不同字长的整数之间转换。
对于无符号数来说,只需要在开头添加 0 即可。
对于有符号数来说,保持补码值的符号扩展,在表示中添加最高有效位的值,比如 [101] 扩展成 [1101]。

截断

截断就是减少表示一个数字的位数。
对于无符号数来说就是减去高位,相当于就是取模运算,模的大小就是剩余位数的最大值了。
对于有符号数来说,和无符号数类似,只不过要把最高位转化为符号数而已。