月亮不会奔你而来,星星也不会,显然我也不会
文章目录
1. Integer.highestOneBit
public static int highestOneBit(int i) {
i |= (i >> 1);
i |= (i >> 2);
i |= (i >> 4);
i |= (i >> 8);
i |= (i >> 16);
return i - (i >>> 1);
}
目的
可以认为这个方法取得是最高位的权重,比如,3的二进制是000…11,调用该方法后返回的就是000…10,也就是十进制的2,如果参数是-1,那最后的返回值就是Integer.MIN_VALUE
代码思路
以二进制00101001为例,两个步骤:
- 将最高位及其之后的位全部填充1,得到00111111
- 无符号右移一位后得00011111,然后00111111-00011111=00100000,也就是最终结果
关键步骤是第一步,我们看代码具体是怎么操作的。首先要明确一点,因为一直是做的或
操作,所以当一位值为1
的时候,他就会一直是1
我们依然拿8位二进制举例,取极端情况10000000
i |= (i >> 1)
可以认为是将某一位的1延伸到其后面一位,比如我们就得到了11000000
i |= (i >> 2)
可以认为是将某一位的1延伸到其后面两位,再配合第一步,就相当于一共覆盖了4位,比如我们现在就得到了11110000
- 依法炮制,我们这次得到了
11111111
- 无符号右移,高位补零,得到
011111111
- 易得结果
也正因为Java的int是32位的,所以它的最后一次是右移16位
2. Integer.lowestOneBit
public static int lowestOneBit(int i) {
return i & -i;
}
目的
与highestOneBit
相对应,这里取得是最低位
代码思路
首先,Java的int类型是补码表示的,补码取复数的操作是取反之后加1
,对于数字x
中最低位的1,其右侧全部为0,取反之后,右侧全为1,其本身变为0,而其左侧则变为原来值的取反,显然取and之后就得到了该位
比如8位的二进制数字10101000
- 取反得
01010111
- 加1之后得
01011000
(观察到其最低位1的右侧全为0,而左侧变成原来值的取反) - 取and,得
00001000
3. Integer.numberOfLeadingZeros
public static int numberOfLeadingZeros(int i) {
if (i == 0)
return 32;
int n = 1;
if (i >>> 16 == 0) { n += 16; i <<= 16; }
if (i >>> 24 == 0) { n += 8; i <<= 8; }
if (i >>> 28 == 0) { n += 4; i <<= 4; }
if (i >>> 30 == 0) { n += 2; i <<= 2; }
n -= i >>> 31;
return n;
}
目的
求前导零的个数
代码思路
- 判断前16位是否为零,如果是,记录下来,左移16位避免重复计算
- 判断前8位是否为零,如果是记录下来,左移8位避免重复计算
- 巴拉巴拉巴拉以此类推
注意,n -= i >>> 31
有什么意义?因为代码中最后之判断了前2位是否为零,并没有包含对第一位是否为零的判断,从int n = 1
中可以看出它是默认第一位是0的,而最后那一句的意义就是在首位为1的情况下把结果做一个矫正。
下面这段代码其实更符合逻辑的一致性,更容易理解
static int numberOfLeadingZeros(byte i) {
if(i==0)return 8;
int ret = 0;
if(i>>>4 == 0){ret+=4;i<<=4;}
if(i>>>6 == 0){ret+=2;i<<=2;}
if(i>>>7 == 0){ret+=1;}
return ret;
}
4. Integer.numberOfTrailingZeros
public static int numberOfTrailingZeros(int i) {
// HD, Figure 5-14
int y;
if (i == 0) return 32;
int n = 31;
y = i <<16; if (y != 0) { n = n -16; i = y; }
y = i << 8; if (y != 0) { n = n - 8; i = y; }
y = i << 4; if (y != 0) { n = n - 4; i = y; }
y = i << 2; if (y != 0) { n = n - 2; i = y; }
return n - ((i << 1) >>> 31);
}
目的
求末尾的0的个数
代码思路
看过了求前导零的方法,这个方法就非常容易理解了,但是看起来这两个方法的风格有点不太一样,而且这个return
有点酷炫
这里我按照求前导零代码的风格写了一个,结果是一样的
static int numberOfTrailingZeros(int i) {
if(i==0)return 32;
int ret = 31;
if(i<<16 != 0){ret-=16;i <<=16;}
if(i<<8 != 0){ret-=8; i<<= 8;}
if(i<<4 != 0){ret-=4;i<<=4;}
if(i<<2!=0){ret-=2;i<<=2;}
if(i<<1!=0){ret--;}
return ret;
}
5. Integer.toString(int i, int radix)
目的
将一个int
数值转化成对应进制的字符串
代码思路
代码思路其实很简单,就是不断取余+除,只是人家写的看起来比较厉害
public static String toString(int i, int radix) {
// 进制范围2-32,非法参数就取10
if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX)
radix = 10;
/* Use the faster version */
if (radix == 10) {
return toString(i);
}
// 32位int加一个符号位
char buf[] = new char[33];
boolean negative = (i < 0);
int charPos = 32;
if (!negative) {
i = -i;
}
// 不断取余,填充最低位
while (i <= -radix) {
buf[charPos--] = digits[-(i % radix)];
i = i / radix;
}
// 处理小于进制数值的最高位
buf[charPos] = digits[-i];
// 添加符号位
if (negative) {
buf[--charPos] = '-';
}
return new String(buf, charPos, (33 - charPos));
}