十进制转二进制(位运算)

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();
}

唔 比内置的实现容易懂,当然效率上肯定会差一些。

posted @ 2020-03-16 18:41  代码森林  阅读(969)  评论(0编辑  收藏  举报