bitXor
思路
如果
x
和
y
相同,
(~x&~y)
和
(x&y)
一定是完全相反的,那么
&
操作就会返回
0.
在其他情况下,在
~(~x&~y)
和
~
(x&y)
中一定会有重叠的
1
核心代码
(~(~x&~y)&~(x&y))
thirdBits
思路
要求生成
3
位一个
1
的掩码。最一开始想到的是将初始的
8
位掩码
0x49
每次向前移动
3
和
4
的公倍数
12
,后来发现
只需要每次向前移动
9
位,多余的
0
通过移位产生
核心代码
int x = 0x49 ;int s = x<< 9 ;int m = x+s;int ans = (m<< 18 )+m;
fitsShort
思路
如果要判断某个数是否可以用
16
位表示,只要判断前
17
位是否为全
0
或全
1
即可
核心代码
x=x>> 15 ;return !((x>> 16 )^x);
isMax
思路
tmax+tmax+2 = 0
注意:
0xffffffff
也满足
x+x+2=0
的条件,此时需要找到一个只有
0xffffffff
表达式为
0
其余都不为
0
的情况,即
x+1
核心代码
int m = x+1 return !((m+m)|!m)
fitsBits
思路
判断
x
是否可以用
n
位表示。思路与
fitsShort
相似,若前
33-n
位全为
0
或
1
,则可以用
n-bit
表示
细节:
~n = -n-1
先把最后
n
位移到最前面,再移回原位,这样就做到把最前面
32-n
位清零或变
1
核心代码
int t = 33 +~n;int a = !(((x<<t)>>t)^x);return a;
upperBits
思路
要求生成前
n
位都是
1
的掩码
本想直接将
0x80000000
后移
n-1
位,发现
n=0
时要移动
-1
位,无法操作。所以特殊化
n=0
的情况
优化:
n+(~0)=n-1
,优于
n+(~1)+1
核心代码
int b = n+(~ 0 ); //b=n-1int mask = ((!(!n))<< 31 )b;// 非 0:0xffffffff;0:0// 这一步非常关键,把非 0 和 0 的操作区别开来
anyOddBit
思路
要求判断奇数位是否有
1
,通过移位生成只有奇数位有
1
的掩码并
&x
第一次写的问题出在:
mask&x
是一个数字,不是
1
,所以!!一下
核心代码
int a = mask&x; // 如果奇数位有 1 ,则 a 不是 0 ,否则 a 是 0return !!a;
byteSwap
思路
利用亦或运算重要性质:
a^(a^b)=b; b^(a^b)=a
把需要交换的位数移动到最低
8
位并进行亦或,然后将亦或后的部分移动到原来的位置,再次进行亦或就可以进行交换
细节:
n*2^m = n<<m
核心代码
int a = x>>n;int b = x>>m; // 把需要交换的位数移动到最低 8 位int axorb = (a^b)&mask; // 得到最低 8 位的 a^bint ans = x^((axorb<<n)|(axorb<<m)); //x^0=x,a^(a^b)=b; b^(a^b)=areturn ans;
absVal
思路
先判断是否为负数,若为负数,取反加一
当函数对正数和负数的操作不同时,需要先用
x>>31
判断正负
x^1
取反,
x^0
不变,可以用来对正负数区分操作
核心代码
int a = x>> 31 ; //x>0,a=00...0;x<0,a=11...1int b = (x^a)+(a& 1 ); // 补码转真值:取反加一return b;
divpwr2
思路
要求计算
x/(2^n)
,通过右移
n
位得到
x/(2^n)
注意:
round to 0,
因为正数和负数
round to 0
时进行的操作不同,所以负数需要加上
mask
:把低于
n
位的数字置为1
核心代码
int N = 1 <<n;int judge = x>> 31 ;int mask = judge&(N+(~ 0 ));return (x+mask)>>n;
float_neg
思路
首先判断是否为
NaN->
阶码位是否全为
1
且小数位不全为
0
若是
NaN->
直接返回
uf
若不是
NaN->
相反数且不是
0
:符号位取反
优化:我本来想的是通过移位操作分别判断
exp
是否全为
1
且
frac
是否不全为
0
,但是在上了周日的课程之后忽然 醒悟,可以把小数当成二进制整数去理解。exp
全为
0
且
frac
不全为
0
用二进制理解就是
uf>0x7f800000
核心代码
if (xsign> 0x7F800000 )return uf;ans = uf^ 0x80000000 ;
logicalNeg
思路
全
1+1=0
,全
0+0=1
,所以只需要把非
0
转化为全
1
,全
0
依旧是全
0
就可以了:把非
0
的首位变为
1
然后移位
-
正数
=
负数,
-0=0
:通过正负数取或来将非
0
的首位变为
1
同时
0
不变
注意:
如何获得全
1
:
~0
,
-1
(
~1+1
)
最后要返回一个数值
1
或
0
,因此不能单纯地
&
一个全
1
核心代码
int a = ((x|(~x+1))>>31)+1;
bitMask
思路
要求:将
mask
从
lowbit
到
highbit
都置为
1
考虑两种情况:
lowbit<highbit/lowbit>highbit
具体操作:把
[highbit,32]
和
[0,lowbit-1]
置为
0
注意:如果
highbit
为
31
:从
a<<(highbit+1)
改为
a+(1<<highbit<<1).
用加法替换
<<
符
(
后直接用
2<<highbit
替换)
核心代码
int a = ~ 0 ; // 生成全 1int ans = (a<<lowbit)&(a+( 2 <<highbit));
isGreater
思路
y-x<0->x>y
注意:
tmin
的问题:
tmin
取反加一还是它本身
,
所以要加上对于
xy
的正负号判断
核心代码
int a = y+~x+ 1 ;return (sign_a& 1 )^(!(sign_a^sign_x)&(sign_a^sign_y));
logicalShift
思路
要求强制进行逻辑移位。思路是生成一个前
n
位为
0
的掩码,清空算术移位造成的
1
注意:生成掩码时,先移动再取反,不然左移最低位会变成
0
核心代码
int m = 0x1 << 31 >>n<< 1 ; // 先移动再取反,不然左移最低位会变成 0int mask = ~m; //mask = 0...0111...1int x_n = x>>n; //x 进行算术移位int ans = x_n&mask; // 把算术移位造成的 1 清空
satMul2
思路
考虑什么情况下会溢出:最高位与次高位不同
没有
if
如何进行分支:预先算出两种情况需要的操作然后用
c& + ~c&
使得进行一种操作的时候另一种为
0
注意:溢出时出现的情况是不确定的,尽量不使用溢出对数据进行操作
核心代码
int a = x<< 1 ; // 正常:直接乘 2; 溢出:顺便获取第 2 位int b = a>> 31 ; // 正数: b=1111; 负数: b=0000int c = (x^a)>> 31 ; // 溢出: c=1111; 正常: c=0000;int tmin = 1 << 31 ; //tmin=1000int ans = ((~c)&a)|(c&(tmin^b)); // 正数 :b^tmin=0111(tmax); 负数: b^tmin=1000(tmin)// 如果不溢出就走前面的,溢出就走后面的;正数与负数的溢出通过 b^tmin 来控制
subOK
思路
负数减正数溢出:差大于
0
;正数减负数溢出:差小于
0
只有异号数相减才可能溢出
核心代码
int diff = x+(~y)+ 1 ;int ans = !((sign_x^sign_y)&(sign_x^sign_d));
trueThreeFourths
思路
先除以四再乘三,虽然这样会丢失精度,但为了防止溢出不得不出此下策
注意:不能用
1/2+1/4=3/4
。这个式子虽然在数学上是成立的,但是在舍入的时候会造成错误
round to 0
的操作:正负数操作不同:正数向下取整,负数向上取整。向下取整其实不用操作,但是向上取整需要在除四前加三
核心代码
int f = x& 0x3 ;int sign = (x>> 31 )& 0x3 ;int frac = (f+f+f+sign)>> 2 ;return temp + frac;
isPower2
思路
如果是
2
的倍数,则
x
只有一个
1
,即
x&(x-1)=0
特殊情况:只有符号位为
1
的负数和全
0
核心代码
int a = x&(x+(~ 0 )); //ispower2,Tmin,0->0int sign = x>> 31 ; // 正数: 0 ;负数:全 1int iszero = !x; // 全 0 : 1 ;非全 0 : 0int ans = a|sign|iszero; // 只有 ispower2 时这三者全是 0return !ans;
float_i2f
思路
float x=(-1)^signbit * 2^(exp-Bias)(1+frac)
frac
:丢弃头
1
并在尾部补
0
向偶舍入
:
首先
32-23=9
,所以只有最后
9
位需要舍入:后
9
位
>100000000,
显然进位
;
后
9
位
=100000000,
则判断第
10
位是否为
1
特殊情况:
0
不参与浮点数表示,直接返回
0
核心代码
int exp = 158 -calzero;int flag = 0 ;if ((temp& 0x1ff )> 0x100 )flag = 1 ;if ((temp& 0x3ff )== 0x300 )flag = 1 ;unsigned int ans = (sign<< 31 )+( exp << 23 )+(temp>> 9 )+flag;
howManyBits
思路
先测前
16
位有没有数字,若有,右移
16
位,在前
16
位中操作找出具体有多少
;
若无,考虑后十六位中的前
8
位
特殊情况:
0
也需要
1
位(用或来实现)
,
负数只需要前面有一个
1
核心代码
int temp = x^(x<< 1 ); // 第一个有效数字前面一定全是 1 或者 0 ,所以抑或一下把第一个有效数字之前的位数置空int move_16 = !(!(temp>> 16 ))<< 4 ; // 如果前 16 位不为 0 ,则 move16, 否则 move0...temp = move_16+move_8+move_4+move_2+move_1+ 1 ; // 除非全是 0 不然 move_1 肯定有一个 1 ,加上的 1 是符号位
float_half
思路
首先,判断
NaN.
另外,虽然都是
uf
,但是还是需要留出符号位
0.5f=f*1/2=exp-1
但也有一种可能,
exp
太小了,乘以
1/2
就溢出了,此时的操作:负数:返回
tmin;
正数:对
frac
进行操作:将
frac乘以1/2
核心代码
unsigned int sign = uf& 0x80000000 ; // 获取符号位unsigned int exp = uf& 0x7f800000 ; // 获取阶码位int a = ((uf& 3 )== 3 ); // 判断 uf 的最后两位是不是 11 ,如果是则 a=1 (用来 round to 0)if ( exp == 0x7f800000 ) // 如果阶码位全 1 ,返回本身return uf;if ( exp <= 0x800000 ) //exp 太小:负数:返回 tmin; 正数:对 frac 进行操作:将 frac 乘以 1/2return sign|(((uf^sign)+a)>> 1 );if ( exp )return uf -0x800000 ; // 正常情况, exp-1