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,labs和llabs函数计算的整数的绝对值j.如果无法表示结果,则行为未定义.
及其脚注:
最负数的绝对值不能用二进制补码表示.
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)
显然,数学上,| -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)
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)
你得到原来的号码.