我刚开始阅读《黑客的乐趣》,它将abs(-231)定义为-231。为什么呢?
我在几个不同的系统上尝试了printf("%x", abs(0x80000000))
,结果都是返回0x80000000。
我刚开始阅读《黑客的乐趣》,它将abs(-231)定义为-231。为什么呢?
我在几个不同的系统上尝试了printf("%x", abs(0x80000000))
,结果都是返回0x80000000。
实际上,在C语言中,这种行为是未定义的。根据C99标准的第7.20.6.1/2节:
abs
、labs
和llabs
函数计算整数j
的绝对值。如果结果不能被表示,则行为未定义。
其注释如下:
在二进制补码表示法下,最负的数的绝对值不能被表示。
对于32位数据类型,没有表达+2^31的方式,因为最大的数字是2^31-1...请阅读有关二进制补码的更多信息...
由于整数在内存中以二进制补码形式存储,因此最小值的正版本会溢出并返回负数。
也就是说(在.NET中,但仍然适用):
int.MaxValue + 1 == int.MinValue // Due to overflow.
并且
Math.Abs((long)int.MinValue) == (long)int.MaxValue + 1
0x80000000
表示“负零”(即零),0x00000000
表示“正零”或普通零。在此方案中,最大正数是0x7fffffff
(2147483647),最小负数是0xffffffff
(−2147483647)。这种方案的优点是易于“解码”,并且是对称的。这种方案的缺点是当a
和b
为不同符号时计算a + b
是一个特殊情况,必须特别处理。0x7fffffff
(2147483647),最小负数是0x80000000
(−2147483647)。0有两种表示:正零为0x00000000
,负零为0xffffffff
。这种方案也存在涉及负数的计算问题。0x00000000
。最大正数是0x7fffffff
(2147483647),最小负数是0x80000000
(−2147483648)。这种表示中存在不对称性。这种方案的优点是无需处理负数的特殊情况。只要结果没有溢出,该表示法就会给出正确的答案。因此,大多数当前的硬件都使用这种表示法来表示整数。在二进制补码表示中,没有办法表示231。实际上,如果您查看编译器的limits.h
或等效文件,您可能会看到对INT_MIN
的定义如下:
#define INT_MIN (-2147483647 - 1)
#define INT_MIN -2147483648
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
你会得到原始数字。
这与数字的存储方式有关。
负数使用二进制补码表示。算法如下...
先将所有位取反,再加1。
以八位数为例...
+0 = -0
00000000 -> 11111111, 11111111 + 1 = 100000000
(但由于比特限制,结果变为00000000)。
还有...
-128 [也称为-(2^7)] 等同于 -(-128)
10000000 -> 01111111, 01111111 + 1 = 10000000
希望这可以帮到你。
我认为abs
的工作方式是首先检查数字的符号位
。如果它是清晰的,那么不做任何操作,因为数字已经是+ve
,否则返回数字的2's complement
。在你的情况下,数字是-ve
,我们需要找到它的2's complement
。但是0x80000000
的2's complement恰好是0x80000000
本身。
0x8000.. 存储为 10000....(二进制)。这被称为二补数,意思是最高位(左边的那个)用于存储值的符号,负值使用负二进制 -1 进行存储。abs() 函数现在检查signbit,看到它被设置并计算出正值。
现在这又是一个负数,这不是我们想要的,原因是溢出,试试数字 0x9000...即 10010...
这个数字的溢出已经被右侧的0位停止了。
因为它使用负指令来执行此操作。
在《汇编语言艺术》一书中,他们就是这样说的。
如果操作数为零,则其符号不会改变,但这会清除进位标志。对任何其他值进行取反都会设置进位标志。对包含-128的字节、包含-32,768的字或包含-2,147,483,648的双字进行取反不会改变操作数,但会设置溢出标志。Neg 操作总是更新 A、S、P 和 Z 标志,就像您使用 sub 指令一样
来源: http://www.arl.wustl.edu/~lockwood/class/cs306/books/artofasm/Chapter_6/CH06-2.html#HEADING2-313 因此,它将设置溢出标志并保持沉默。这就是原因。