为什么最大负整数-2147483648的绝对值仍然是-2147483648?

29
abs(-2147483648) 的结果是 -2147483648,不是吗?看起来有些不可接受。
printf("abs(-2147483648): %d\n", abs(-2147483648));

输出:

abs(-2147483648): -2147483648

1
我认为这是未定义行为。我手头没有C标准,所以无法支持我的说法。 - Alexandre C.
5
考虑到 abs(int) 返回的是一个 int,你期待它会是什么类型? - Philip Kendall
9
C11的最新草案(7.21.6.1,有关abs和其它函数)中写道:“如果结果无法表示,则行为未定义。” - Alexandre C.
4
Linux手册(man 3 abs)中写道:尝试对最小的负整数取绝对值是没有定义的。 - Pat
如果int有超过32位,那么你会得到正确的值。但对于32位的int,考虑到2147483648无法在32位中表示,你会怎么做呢? - Jim Balter
显示剩余2条评论
6个回答

34
标准关于abs()的说明如下:

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

事实上,结果确实无法表示,因为带符号整数的2's补码表示不对称。想一想... 如果一个int有32位,那么就会得到从INT_MININT_MAX的232个不同的值。这是偶数个值。因此,如果只有一个0,则大于0的值的数量不能与小于0的值的数量相同。因此,没有一个正数可以与值为-INT_MININT_MIN相对应。

所以,在您的平台上调用abs(INT_MIN)是不可接受的。


19

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

将正数转换为负数时,使用逻辑运算。

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了

原问题同样适用


1
这就是为什么在二补数中取反后0和最小值总是相同的原因。 - phuclv

11

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


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

1
我记得这是一个溢出错误。在S2C中,有符号整数的最大值是-(2^(总位数-1))。然而,最大正值实际上是(2^(总位数-1))-1。在位运算中,函数产生了正确的结果。然而,“正确”的位值超过了最大正值一个单位,导致溢出到负值的集合。

1
这可能是某些实现的情况,但标准规定这是未定义行为。在C23之前,整数可以表示为二进制补码、反码或补码和符号。但即使在C23中只允许二进制补码,尝试abs(INT_MIN)仍然是未定义行为。 - undefined

-4

试试这个

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

6
这个,我的朋友,具有未定义的行为。你正在使用无符号格式打印带有符号整数。我减去了-1,因为它并没有回答问题。 - Alexandre C.
问题是要求解释,而不是修复代码的方法。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接