理解整数运算的属性以及溢出的检查

计算机执行的“整数”运算实际上是一种模运算形式。表示数字的有限字长限制了可能的值的取值范围,结果运算可能溢出。补码表示提供了一种既能表示负数也能表示正数的灵活方法,同时使用了与执行无符号算数相同的位级实现,这些运算包括加法,减法,乘法,甚至除法(需要一个偏移量)。无论运算数是以无符号形式还是以补码形式表示的,都有完全一样或者非常类似的位级行为。

C语言中的某些规定可能会产生令人意想不到的结果,尤其是unsigned数据类型。下面通过一个例子,总结一下。

例2.44 假设我们在对有符号值使用补码运算的32位机器上运行程序。对于有符号值使用的是算术右移,而对于无符号值使用的是逻辑右移。变量的声明和初始化如下:

int x = foo();     /* Arbitrary value */
int y = bar();     /* Arbitrary value */

unsigned ux = x;
unsigned uy = y;

对于下面每个C表达式,1)证明对于所有的x和y值,它都为真(等于1):或者2)给出使得它为假(等于0)的x和y的值:

A. (x > 0) || (x-1 < 0)

答案:
假,x = Tmin, 那么x-1就等于Tmax。

B. (x & 7) != 7 || (x << 29 < 0)

答案:
真,如果为假,左移29位后是0x70000000肯定小于0,因此假设不成立。

C. (x * x) >= 0

答案:
假,x=65535 (0xFFFF)时,x*x为-131071(0xFFFE0001)。

D. x < 0 || -x <= 0

答案:
真,如果x是非负数,那么-x肯定是非正数。

E. x > 0 || -x >= 0

答案:
假,x=Tmin。

F. x+y == uy+ux

答案:
真,无符号数和有符号数有相同的加法位级行为。

G. x*~y + uy*ux == -x

答案:
真,~y(y的补)等于-y-1。uy*ux等于x*y。因此等式左边等价于x*-y-x+x*y。

  • 检查加法是否溢出的函数
int tadd_ok(int x, int y)
{
    int sum = x + y;
    if (x >= 0) && (y >= 0) && (sum < 0) /* 注意等号 */
        return 0;
    if (x < 0) && (y < 0) && (sum >= 0)
        return 0;
    return 1;
}

int uadd_ok(unsigned x, unsigned y)
{
    unsigned sum = x + y;
    return sum >= x; /* 如果溢出肯定比任意一个都要小 */
}
  • 检查乘法是否溢出的函数
int tmult_ok(int x, int y) 
{
    int p = x*y;
    return !x || p/x == y;
}

你可能感兴趣的:(C语言)