P60 整数运算
1.无符号数加法
非负整数x,y,满足0<=x,y<2ω,x和y都能表示为ω位的无符号整数。
x+y的范围是[0,2*2ω-2),这就有可能需要ω+1位来表示。
计算机在进行无符号数的加法时,用ω+1位来运算,实际结果是截断此运算结果ω位得到的,即丢弃最高位。
可以分两种情况来看:
1)若x+y的范围仍然是[0,2ω),则此时最高位必定为0,所以丢弃最高位不影响其大小。
2)若x+y超过了2ω,此时最高位必定为1,丢弃最高相当于减去2ω。这就是溢出。
//无符号加法也可以视为一种模运算,加法结果=实际结果mod 2ω
例. x=9,y=12,x和y都可以表示为4位,[1001]和[1100],4位的表示范围为[0,16),而9+12=21,已经超过了4位的表示范围,需要五位来表示,即[10101],丢弃最高位1,得到[0101],表示的无符号数为5,与5=9+12-16一致,也与5=(9+12)mod16一致。
无符号数的加法规则:
x 无符号加 y = x + y,x+y<2ω
x 无符号加 y = x + y - 2ω,x+y>2ω
无符号数溢出的判断
设s = x 无符号加 y,已知x>=0,y>=0,若无溢出,则必有s>=x且s>=y
反之,若有s<x或s<y,则必定发生了溢出。
int uadd_ok(unsigned x,unsigned y){return x+y>=x;}
求无符号数的加法逆元(相反数)
-x=2ω-x,x>0 (x=0时,-x=0)
2.补码加法
整数x,y,满-2ω-1<=x,y<=2ω-1-1,x和y都能用ω位的补码表示。
由于补码的第一位是符号位,所以ω位的补码表示的范围是[-2ω-1,2ω-1-1]
和无符号数一样用截断的方法处理补码加法。
x + y = x + y - 2ω , 2ω-1 <= x + y
x + y = x + y , -2ω-1 <= x + y < 2ω-1
x + y = x + y + 2ω , x + y < -2ω-1
简而言之,正溢出减去2ω,负溢出加上2ω。
检验补码加法是否溢出
当x>0,y>0,有s<0
当x<0,y<0,有s>0
上述两种情况发生溢出。
错误代码
int tadd_ok(int x,int y) { int sum=x+y; return (sum-x==y)&&(sum-y==x); }
上述代码永远返回true
因为即使发生了溢出,但运算是可逆的,sum-x==y恒成立。
补码的加法逆元
当x=-2ω-1,-x=-2ω-1
当x>-2ω,-x=-x
当x为表示范围里的最小值即-2ω-1,-2ω-1+(-2ω-1)= -2ω
发生了负溢出,结果需要加上2ω,所以-2ω-1+(-2ω-1)= -2ω + 2ω = 0,所以-2ω-1的加法逆元就是其自身。
补码取非的技巧
将补码最右边的1的左边所有位按位取反,其他位保持不变。
3.无符号乘法
依然是截断低ω位得到结果
x 无符号* y = (x*y) mod 2ω
4.补码乘法
补码乘法和无符号数乘法的位级表示是相同的
补码乘法的结果就是无符号乘法的结果转为补码。
5.常数乘法
无符号整数乘以2的幂:x*2k,位表示的右边添k个0即可,即左移k位
补码同样左移k位即可。
发生溢出时,只需将移位结果截断即可。
例如,11的位表示为[1011],11*4=11*22,[1011]左移两位得到[101100],截断低4位得到[1100],即12=(11*4) mod 24
位操作在编译器中的应用
乘法的开销比加法和位移大得多,编译器往往希望用加减和位移来代替整数乘以常数的操作。
例如,x*14,会被优化为(x<<3)+(x<<2)+(x<<1)
除以2的幂
无符号除,逻辑右移k位,x>>k
补码除法,偏置