有符号整数在x86上通过二进制补码表示,其中符号位的值为-(2^N)
。这导致典型的可表示值范围介于-2^N
和2^N - 1
之间(例如-32768
到32767
)。
我很好奇如果我在系统上取最小有符号整数值并将其乘以-1
以试图“强制”最大值大于我的有符号整数的最大可表示值时会发生什么系统
#include <stdio.h>
#include <limits.h>
int main(void){
signed int x, y;
x = INT_MIN;
y = x * -1;
printf("%d\n%d\n", x, y);
return 0;
}
这导致以下输出:
# gcc -std=c89 -pedantic int_min_test.c
# ./a.out
-2147483648
-2147483648
我期待整数溢出(导致典型值翻转),但看起来好像没有关于x
与-1
的乘法运算。
x86中INT_MIN
与-1
的乘法是否为无操作?
答案 0 :(得分:5)
使用gcc 4.8.5,使用以下指令计算行y = x * -1;
:
neg %eax
neg操作翻转该字的位然后加1.对于32位2的补码,结果为:
0x80000000 # Start value
0x7FFFFFFF # Flip bits
0x80000000 # Add 1
如您所见,计算机正在按照您的要求进行操作。这不是禁止操作,因为neg
会修改AF
,CF
,OF
,PF
,SF
和{{1}标志。它只是所使用的二进制表示的工件。正如其他人所说,它只是未定义的行为。
答案 1 :(得分:4)
对于C(基于问题的原始标记,以及用C编写的示例代码),它是未定义的行为,所以不,C表达式INT_MIN * -1
的结果是不是&#34;一个no-op&#34;,它是未定义的行为。在实践中,根据您的观察方式,可能会观察到结果不一致,甚至可能在时间上不一致。不要这样做。
如果您想询问x86 imul
指令,那就是一个不同的问题,我相信({1}} imul
0x80000000
0xffffffff
产生0x80000000
。但这与您应该在C中看到的内容无关。
答案 2 :(得分:2)
在x86处理器上,指令将乘以&#34; int&#34; by -1 will, 当给定位模式0x80000000时,产生相同的位模式。 但是,需要注意的是,x86的C编译器可能会被划分 分为若干类别:
在某些编译器上,所有有符号整数运算的行为就像使用精确长度的本机运算执行数学运算一样,这相当于执行具有无限精度的运算并向任何输出中添加或减去范围值无论整数模数的多倍都是必要的,以使其在范围内。这种行为类似于无符号类型的行为,除了范围不同。在这些编译器上,-INT_MIN == INT_MIN
。
在某些编译器中,所有有符号整数运算的行为都表现为无限精度,但可以在整数模数的任意倍数的任何超出范围值中加/减,可能产生结果其行为类似于其类型范围之外的数字。这允许表达像&#34; x + 1&gt; y&#34;要替换为&#34; x&gt; = y&#34;,并且还可以允许代码以不可能包含溢出的方式移动到循环外部。在此类编译器上,-INT_MIN
可能等于INT_MIN
,或者可能等于-(long)INT_MIN
,或者低32位与INT_MIN匹配的任何其他值。一些这样的编译器在将它们存储到变量时会截断超出范围的值,但有些可能不会。这种编译器的一个关键特性是代码不需要防止溢出,如果&#34;额外&#34;在发生溢出的情况下,结果的位将无关紧要[例如, uint1=ushort1*ushort2;
]。
有些编译器会使用整数溢出作为否定时间和因果律的基础。当使用这样的编译器时,必须包括逻辑以防止不惜一切代价溢出,无论生成的机器代码是否对程序的满足要求有贡献。
就个人而言,我认为第二种形式的语义最有意义;它 允许几乎所有可能的有用优化 第三,使程序员能够实现优化 在第三种情况下是不可能的。如果第二种形式下的行为保证足以确保程序即使在发生溢出时也能满足其行为要求,那么强制程序员无论如何都要处理溢出会使代码效率降低。虽然通常会出现溢出检查是一个好主意的情况,即使以降低效率为代价,但我认为有些事情是荒谬的,而且优化&#34;编译器要求程序员编写的代码效率低于其他需要的代码。