abs(-2147483648) 的结果是-2147483648,不是吗?这似乎是不可接受的。
printf("abs(-2147483648): %d\n", abs(-2147483648));
输出:
abs(-2147483648): -2147483648
该标准说abs()
:
、
abs
和函数计算整数labs
的llabs
绝对值j
。如果结果无法表示,则行为未定义。
结果确实无法表示,因为有符号整数的 2 的补码表示不是对称的。想想看……如果你在一个 32 位int
,这给你 2 32 个不同的值从INT_MIN
to INT_MAX
。这是偶数个值。因此,如果只有一个 0,则大于 0 的值的数量不能与小于 0 的值的数量相同。因此没有INT_MIN
与 - 值对应的正数INT_MIN
。
所以,不能接受的是abs(INT_MIN)
在你的平台上打电话。
负数通常用二进制补码表示。
要将正数转换为负数,使用逻辑
x -> not(x)+1
对于 8 位算术
01111111b 是 127,-127 变成
10000000b + 1 = 10000001b
和相反的方向 -127 10000001b 变为
01111110b + 1 = 01111111b
-128 呢?
-128 是 10000000b 并且没有它的正数对应物,因为在 8 位有符号算术中没有 128。
10000000 -> 01111111 + 1 = 10000000 和 -128
同样适用于原始问题
由于 2147483648 大于INT_MAX
您的实现,abs(-2147483648)
因此未定义。
这是 GNU glibc 源代码中 abs.c 中的代码。
/* Return the absolute value of I. */
int
DEFUN(abs, (i), int i)
{
return(i < 0 ? -i : i);
}
所以,abs(-2147483648) 返回 -(-2147483648) 。在 x86 中,它是通过这两条指令实现的
movl $-2147483648, %eax
negl %eax
negl指令是这样实现的:num=0-num; sbb 是通过这种方式实现的:从目标中减去源,如果设置了进位标志,则额外减去 1。所以 abs(-2147483648) (十六进制是 0x80000000 ) --> -(-2147483648) --> 0-(-2147483648) 最终变成 (0x80000000) 。
negl指令详情,请访问http://zsmith.co/intel_n.html#neg
sbb指令详情,请访问http://web.itu.edu.tr/kesgin/mul06/intel/instr/sbb.html
尝试这个
printf("abs(-2147483648): %u\n", abs(-2147483648));