Home
updated:

对一段补码算术代码的分析


#include <stdio.h>
#include <limits.h>
int tsub\_ok(int x, int y); //判断x+y是否溢出,不溢出则返回1
int tadd\_ok(int x, int y); //判断x-y是否溢出,不溢出则返回1
int tadd\_ok(int x, int y)
{
    int sum = x + y;
    if (sum > 0 && x < 0 && y < 0) {
        return 0;
    } else if (sum < 0 && x > 0 && y > 0) {
        return 0;
    } else {
        return 1;
    }
}
int tsub\_ok(int x, int y)
{
    return tadd\_ok(x, -y);
}
int main()
{
    int x = -56;
    int y = INT\_MIN;
    printf("%d\\n", tsub\_ok(x, y));
    printf("%d\\n", b);
    return 0;
}

上面tsub_ok函数的写法看似没有问题,但其实存在一个关于INT_MIN的隐蔽问题。当且仅当x-y中的y取INT_MIN时,无论x取何值,函数的输出都与事实相反。原因正在于补码的不对称性:-INT_MIN并不在补码的范围内,此时就发生了溢出。

溢出的方式也很巧,-INT_MIN的十六进制应该是0x80000000(无符号编码),然而int类型是有符号的,该编码被以补码形式又解释为了INT_MIN。因此,在32位补码的世界里,-INT_MIN = INT_MIN。

因此就出现了这样的情况:y = INT_MIN,x为负值时,程序判断会发生溢出;x为非负值时,程序判断不会溢出,都与事实相悖(注意当x取0时,由于补码的不对称,运算事实上还是会溢出的)。