我刚刚开始阅读Hacker's Delight并将abs(-2 31 )定义为-2 31 。那是为什么?
我在几个不同的系统上尝试了printf("%x", abs(0x80000000))
,并且我在所有系统上都返回了0x80000000。
答案 0 :(得分:42)
实际上,在C中,行为是未定义的。根据C99标准,§7.20.6.1/ 2:
abs
,labs
和llabs
函数计算整数j
的绝对值。如果无法表示结果,则行为未定义。
及其脚注:
最负数的绝对值不能用二进制补码表示。
答案 1 :(得分:14)
对于32位数据类型,没有+ 2 ^ 31的表达式,因为最大数字是2 ^ 31-1 ...详细了解two's complement ...
答案 2 :(得分:10)
因为整数作为二进制补码二进制数存储在内存中,所以最小值的正数溢出回负值。
也就是说(在.NET中,但仍然适用):
int.MaxValue + 1 == int.MinValue // Due to overflow.
和
Math.Abs((long)int.MinValue) == (long)int.MaxValue + 1
答案 3 :(得分:8)
显然,在数学上,| -2 31 |是2 31 。如果我们有32位来表示整数,我们最多可以表示2个 32 数。如果我们想要一个关于0的对称表示,我们做出一些决定。
对于以下内容,如您的问题,我假设32位宽数字。必须使用至少一个位模式为0.因此,对于其余数字,我们留下2 32 -1或更少位模式。这个数字是奇数,所以我们可以有一个关于零不完全对称的表示,或者有一个数字用两个不同的表示来表示。
0x80000000
是“负零”(即,零),并且0x00000000
是“正零”或常规零。在此方案中,最正数为0x7fffffff
(2147483647),最负数为0xffffffff
(-2147483647)。该方案的优点是我们很容易“解码”,并且它是对称的。该方案的缺点在于,当a + b
和a
具有不同符号时计算b
是一种特殊情况,必须特别处理。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)
这样做而不是
#define INT_MIN -2147483648
因为2147483648太大而不适合32位二进制补码表示中的int
。当一元减号运算符“获取”要操作的数字时,为时已晚:溢出已经发生,你无法解决它。
因此,为了回答您的原始问题,二进制补码表示中最负数的绝对值不能用该编码表示。另外,从上面的两个补码表示中得到负值到正值,你可以得到它的补码,然后加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
你得到原始号码。
答案 4 :(得分:3)
这可以追溯到数字的存储方式。
使用二进制补码存储负数。算法就像......
翻转所有位,然后加1。
使用八位数作为例子......
+0 = -0
00000000 - > 11111111,111111111 + 1 = 100000000
(但由于位的限制,这变为00000000)。
和...
-128 [aka - (2 ^ 7)]等于 - ( - 128)
10000000 - > 01111111,01111111 + 1 = 10000000
希望这有帮助。
答案 5 :(得分:3)
二进制补码的表示最高位为负数。 0x80000000是1后跟31个零,第一个1代表-2 ^ 31而不是2 ^ 31。因此,无法表示2 ^ 31,因为最高正数为0x7FFFFFFF,即0,后跟31个,等于2 ^ 31-1。
abs(0x80000000)因此在二进制补码中未定义,因为它太大,因此机器只是放弃并再次给你0x80000000。通常至少。
答案 6 :(得分:1)
我认为abs
的工作方式是先检查号码的sign bit
。如果数字已明确无效,则+ve
否则返回该数字的2's complement
。在您的情况下,该数字为-ve
,我们需要找到其2's complement
。但0x80000000
的2的补码恰好是0x80000000
本身。
答案 7 :(得分:1)
0x8000 ..存储为10000 ....(二进制)。这被称为二进制补码,这意味着最高位(左边的位)用于存储值的符号,负值用负二进制存储 - 1. abs()函数现在检查signbit,看到它被设置并计算正值。
现在这是我们不想要的负数,原因是溢出,尝试数字0x9000 ......这是10010 ......
使用此数字,右边的0位停止溢出
答案 8 :(得分:0)
因为它使用neg指令来执行此操作。
在汇编语言程序设计书中,他们就这样说过。
如果操作数为零,则其符号为 没有改变,虽然这清除了 携带国旗。否定任何其他价值 设置进位标志。否定一个字节 包含-128,包含一个单词 -32,768,或包含-2,147,483,648的双字不会更改操作数,但会设置溢出 旗。 Neg总是更新A,S,P, 和Z标志,就好像你正在使用 子指令
来源:http://www.arl.wustl.edu/~lockwood/class/cs306/books/artofasm/Chapter_6/CH06-2.html#HEADING2-313 所以它会设置溢出标志并且是静默的。这就是原因。