十进制转二进制(位运算)
Java内置了API: Integer.toBinaryString();
先看源码是怎么写的
private static String toUnsignedString0(int val, int shift) {
// assert shift > 0 && shift <=5 : "Illegal shift value";
int mag = Integer.SIZE - Integer.numberOfLeadingZeros(val);
int chars = Math.max(((mag + (shift - 1)) / shift), 1);
char[] buf = new char[chars];
formatUnsignedInt(val, shift, buf, 0, chars);
// Use special constructor which takes over "buf".
return new String(buf, true);
}
核心是两部分
一计算mag,mag指的是二进制需要占用的位数, eg: 10 => 1010 需要四位。
另一个就是formatUnsignedInt(); 去转换,等下去具体看。
public static int numberOfLeadingZeros(int i) {
// HD, Figure 5-6
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;
}
这个方法里都是左移右移的位运算,左移就是末尾加0 即乘以2,eg: 10 <<= 1 即 10100 等于20。右移是反过来 10 >>= 1 即 101 等于5。
所以这几个if是在干嘛呢,举个例子理解,以10为例,我们知道它的二进制是四位,不够十六位,所以再过了第一个if之后会变成1010...(16),其他几个类似,你可以在脑子里过一遍。首先我们确定i最大是31位,而规律就是过完这些if,i 要么是32位要么是31位,那么它再右移31位 结果要么是0要么是1。而n就记录了这个过程中实际补了多少位。我只能分析出这个规律,不能想出如何想到的这样的想法来计算。我们的例子10 对应结果 n = 28; mag = 4; 可以脑补下它执行了哪几个if。
static int formatUnsignedInt(int val, int shift, char[] buf, int offset, int len) {
int charPos = len;
int radix = 1 << shift;
int mask = radix - 1;
do {
buf[offset + --charPos] = Integer.digits[val & mask];
val >>>= shift;
} while (val != 0 && charPos > 0);
return charPos;
}
这是第二部分真正去转换的部分,猛一看不懂这些变量radix mask代表什么意思,但是我们通过debug将值带入进去发现mask = 1。然后得到转换值的关键在于digits[val & mask], digits是定义的一个数组存放了[0-9][a-z]。然后 奇数&1 = 1; 偶数 & 1 = 0; 为什么会这样,还是以10为例 1010 & 0001 只比较最后一位即可 因为任何数 & 0都为0,所以 0 & 1 = 0;实际上digits[0] = 0; digits[1] = 1; 我们可以找到这个方法的核心将最后一位 & 1 然后右移移除最后一位。
我最后再想一下平时的写法
public static String int2Binary(int n) {
StringBuilder result = new StringBuilder();
while (n > 0) {
if (n % 2 == 0) {
result.append("0");
} else {
result.append("1");
}
n = n / 2;
}
return result.reverse().toString();
}
唔 比内置的实现容易懂,当然效率上肯定会差一些。
你的点赞是莫大的鼓励和博主前进的动力 |