25

abs(-2147483648) 的结果是-2147483648,不是吗?这似乎是不可接受的。

printf("abs(-2147483648): %d\n", abs(-2147483648));

输出:

abs(-2147483648): -2147483648
4

5 回答 5

30

该标准说abs()

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

结果确实无法表示,因为有符号整数的 2 的补码表示不是对称的。想想看……如果你在一个 32 位int,这给你 2 32 个不同的值从INT_MINto INT_MAX。这是偶数个值。因此,如果只有一个 0,则大于 0 的值的数量不能与小于 0 的值的数量相同。因此没有INT_MIN与 - 值对应的正数INT_MIN

所以,不能接受的是abs(INT_MIN)在你的平台上打电话。

于 2012-06-28T13:30:49.000 回答
18

负数通常用二进制补码表示。

要将正数转换为负数,使用逻辑

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

同样适用于原始问题

于 2012-06-28T14:23:38.180 回答
10

由于 2147483648 大于INT_MAX您的实现,abs(-2147483648)因此未定义。

于 2012-06-28T11:17:19.790 回答
5

这是 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

于 2012-06-28T13:14:49.633 回答
-5

尝试这个

printf("abs(-2147483648): %u\n", abs(-2147483648));
于 2012-06-28T11:10:40.207 回答