问:

无符号数-1-3的结果是什么?


一、负数

首先需要清楚-1和-3在计算机中是如何表示的:负数在计算机中的二进制使用补码表示,将其对应正数按位取反再加1。在64位系统中使用unsigned int表示时,-1的十六进制值为0xffffffff,即在二进制中的全位1。

wiki中对于补码的介绍是:

补码系统的最大优点是可以在加法或减法处理中,不需因为数字的正负而使用不同的计算方式。

要理解这句话首先要知道在负整数中最大的值是-1,而在正整数中1是最小的值

如果用惯性思维思考负数的表示,单纯的使用最高位取1表示负数,那么此时-1的二进制(以下二进制都简化为1字节方便计算)为1000 0001,此时计算1000 0001 - 1的值就变成了1000 0000即-0(使用上面的表示方法时),显然是有问题的,而为了解决这个问题就必须单独设计一套在负数情况下的二进制计算逻辑。

如果利用补码代表负数时,上面的计算可以表示为1111 1111 - 1 = 1111 1110即补码表示的-2,这样一直减下去,当到达1000 0000的时候就能表示最小的负值,这与我们一般认知的负数的特性相同,且此时可以发现正数和负数的计算逻辑其实是一致的,只需要一套加法逻辑和一个求补码逻辑就能解决所有加减运算。

我们可以得出-1和-3在计算机中使用补码形式(为了简化使用由二进制转换的十六进制表示)表示分别为:0xffffffff0xfffffffc


二、无符号数溢出

知道-1和-3的二进制之后我们就可以计算他们相加的值了,我们将二进制表示简化为1个字节来看此时的运算过程:

-1: 1111 1111
-3: 1111 1101  +
-----------------
  1 1111 1100

可以发现这两个数二进制相加的值超出了64位无符号整型所能表示的最大值,此时应该发生了溢出。

但在,C语言中规定:

C/C++语言规定无符号整数运算不存在溢出,如果结果超出了无符号类型能表示的最大数,则做模运算取余数。例如,对于uint32的 2-3, 其结果对0x10000模运算取余数,最终结果为0xFFFF。

但是为什么是求模取余呢?为什么是对0x10000进行模运算?

其实在C语言中对无符号数溢出计算规定的原文是:

§6.2.5/9: A computation involving unsigned operands can never overflow,because a result that cannot be represented by the resulting unsigned integer type is reduced modulo the number that is one greater than the largest value that can be represented by the resulting type

所谓is reduced modulo the number that is one greater than the largest value that can be represented by the resulting type就是指:溢出的值要对无符号整型在计算机所能表示的最大数大1的值取模

我们来看上面简化后计算得到的二进制值结果:1 1111 1100,此时无符号整型能表示的最大值是1111 1111,比这个数大1的数为,1 0000 0000,此时我们就需要计算出1 1111 1100 % 1 0000 0000的值。

那该如何计算这他们的模呢?


三、模除运算

计算机中的模运算可以通过依次计算带余数的除法实现取模操作,所谓模除运算就是:

被除数 a 和除数 na modulo n (缩写为 a mod n)得到的是使用欧几里德除法时 a/n 的余数。

普通的计算中我们可以将模运算等价的表示为:a % n == a - (n * int(a/n))

但是,当除数n的值正好为2的倍数时,模运算可以简化为逐位与运算:a % 2n == a & (2n - 1)

此时我们再看is one greater than the largest value这句话就能理解了,进行这个模运算的本意其实就是将最高位产生的进位1抹去。

此时我们再开始进行计算:

1 1111 1100 % 1 0000 0000 == 1 1111 1100 & (1 0000 0000 - 1)

即:
1 1111 1100
0 1111 1111 &
-------------
0 1111 1100

得到的值则为:1111 1100,将其拓展到64位系统中则其十六进制值就是0xfffffffc

由此我们终于明白无符号整型的-1-3计算得到的结果是4294967292(十六进制0xfffffffc)。


四、End

数学,很奇妙吧:)



Reference: