为什么abs(0x80000000)== 0x80000000?

sig*_*ice 21 c integer 32-bit bit-manipulation

我刚刚开始阅读Hacker's Delight,它将abs(-2 31)定义为-2 31.这是为什么?

我尝试printf("%x", abs(0x80000000))了几个不同的系统,我在所有系统上都找到了0x80000000.

Jam*_*lis 42

实际上,在C中,行为是未定义的.根据C99标准,§7.20.6.1/ 2:

abs,labsllabs函数计算的整数的绝对值j.如果无法表示结果,则行为未定义.

及其脚注:

最负数的绝对值不能用二进制补码表示.

  • 绝对+1指出整个事物的不确定性,而不是去解释某个平台刚刚发生的事情. (4认同)

tan*_*ius 14

对于32位数据类型,没有+ 2 ^ 31的表达式,因为最大数字是2 ^ 31-1 ...阅读更多关于二者的补码 ...

  • @sigjuice:32位数据类型的范围是-2 ^ 31到2 ^ 31-1 ...所以,是的,2 ^ 31没有表达式 - 它会导致溢出 (4认同)

Joh*_*zen 10

因为整数作为二进制补码二进制数存储在内存中,所以最小值的正数溢出回负数.

也就是说(在.NET中,但仍然适用):

int.MaxValue + 1 == int.MinValue  // Due to overflow.
Run Code Online (Sandbox Code Playgroud)

Math.Abs((long)int.MinValue) == (long)int.MaxValue + 1
Run Code Online (Sandbox Code Playgroud)


Alo*_*hal 8

显然,数学上,| -2 31 | 是2 31.如果我们有32位来表示整数,我们最多可以表示2 32个数字.如果我们想要一个关于0的对称表示,我们做出一些决定.

对于以下内容,如您的问题,我假设32位宽数字.必须使用至少一个位模式为0.因此,对于其余数字,我们留下2 32 -1或更少位模式.这个数字是奇数,所以我们可以有一个关于零不完全对称的表示,或者有一个数字用两个不同的表示来表示.

  • 如果我们使用符号幅度表示,则最高有效位表示数字的符号,其余位表示数字的大小.在该方案中,0x80000000是"负零"(即,零),并且0x00000000是"正零"或常规零.在该方案中,最正数是0x7fffffff(2147483647),最负数是0xffffffff(-2147483647).该方案的优点是我们很容易"解码",并且它是对称的.该方案的缺点在于,计算a + b何时ab不同的标志是特殊情况,并且必须特别处理.
  • 如果我们使用一个补码表示,最重要的位仍代表符号.正数将该位设为0,其余位构成数字的大小.对于负数,您只需将相应正数表示的位反转(使用一长串的一系列补码 - 因此名称为'补码).在该方案中,最大正数仍为0x7fffffff(2147483647),最大负数为0x80000000(-2147483647).还有两个0的表示:正零0x00000000和负零0xffffffff.该方案还存在涉及负数的计算问题.
  • 如果我们使用二进制补码方案,则通过采用补码表示并添加1它来获得负数.在这个方案中,只有一个0,即0x00000000.最正数是0x7fffffff(2147483647),最负数是0x80000000(-2147483648).这种表现形式存在不对称性.这种方案的优点是人们不必处理负数的特殊情况.只要结果不溢出,表示就会给你正确的答案.因此,大多数当前硬件表示此表示中的整数.

在二进制补码表示中,没有办法表示2 31.实际上,如果查看编译器limits.h或等效文件,可能会INT_MIN以这种方式看到定义:

#define INT_MIN (-2147483647 - 1)
Run Code Online (Sandbox Code Playgroud)

这样做而不是

#define INT_MIN -2147483648
Run Code Online (Sandbox Code Playgroud)

因为2147483648太大而不适合int32位二进制补码表示.当一元减号运算符"获取"要操作的数字时,为时已晚:溢出已经发生并且您无法修复它.

因此,为了回答您的原始问题,二进制补码表示中最负数的绝对值不能用该编码表示.另外,从上面的两个补码表示中得到负值到正值,你可以得到它的补码,然后加1.这样,对于0x80000000:

1000 0000 0000 0000 0000 0000 0000 0000   original number
0111 1111 1111 1111 1111 1111 1111 1111   ones' complement
1000 0000 0000 0000 0000 0000 0000 0000   + 1
Run Code Online (Sandbox Code Playgroud)

你得到原来的号码.