着一章主要介绍了数据才计算机里面的二进制存储,一些位运算的小技巧和一些程序中可能出现的错误。
[0]
高位取反低位不变:利用异或x^(~0xff)
[1] 利用位运算交换两个数:
y = x^y
x = y^x
y = y^x
注意,这一段代码存在一个隐藏的陷阱,如果x,y同时指向一个变量,那么第一步就会把这两个变量同时变成0,这样后面的答案全没有意义了。所以要注意这一点。
[2] 无符号和有符号之间的隐式转换造成的错误。
像下面这样的代码,如果length是无符号,那么-1这个运算就可能会产生错误。因为length可能会是0。所以比较好的习惯是写为i<length
.
for(int i = 0;i<=length-1;++i){}
又比如下面这个例子:
int longer(char* s1,char* s2){
return strlen(s1)-strlen(s2)>0;
}
由于strlen()返回的是size_t,而size_t是一个无符号整数,那么得到的结果就可能出错,所以通过这两个例子就可以得出一个结论:无符号整数不要轻易的进行减法运算。
[3]补码的溢出情况
补码有两种溢出情况,分别是正溢出和负溢出。假设整数是w位,那么对于 −2w−1≤x,y≤2w−1−1
[4]乘以常数
编译器有时候会优化成 a<<k1+a<<k2+a<<k3 …的形式。对于LEA指令,可以计算 a<<k+b ,考虑b是0或者a,我们可以利用一条LEA指令就计算出a的 2k或者2k+1倍。
d
[5]整数的运算
对于无符号数以及有符号数中的正数,除以 2k 可以写成 a>>k ,对于负数,我们需要写成 (a+2k−1)>>k ,第二项的式子很容易通过把a写成 a=k∗y+r 来证明是正确的。
如果我们对于一个整数进行相应的2幂次除法,如何在不使用比较和判断符号的情况下完成上述的运算?如果这个整数是32位的,我们可以利用x = a>>31来获取答案。对于正数和负数,产生的结果是全1或者全0。
习题:
显然M=32,N=8。
习题:
这道题主要搞清楚溢出的情况,前面的答案分别是假,真,假,真,假,真,真。要注意补码运算和无符号数的运算在位运算截断之后是等价的,所以F正确。而对于G,因为~y = -(y+1),所以原来的式子就变成 x∗(−y−1)+x∗y=−x ,所以正确。
[6]浮点数的运算
(1)一个浮点数计算二进制的表示
x = 0.1
for i in range(20):
x *= 2
if x>1:print('1',end='')
else:print('0',end='')
x = x-1 if x>1 else x