美文网首页JDK源码分析
Integer之numberOfLeadingZeros

Integer之numberOfLeadingZeros

作者: cosfun | 来源:发表于2020-02-26 18:14 被阅读0次

    源码为

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

    该方法的作用是将i的二进制表示的数前面的0的个数返回,
    由于负数第一位符号位是1,所以必然返回0

         if (i >>> 16 == 0) { n += 16; i <<= 16; }
    

    i >>> 16,这表示i向右移动16位,与i>>16不同
    '>>右移时如果是正数会补0,负数会补1,而>>>只会补0,举例

    8>>2  =  0100 -> 0001
    8>>>2 = 0100 -> 0001
    -8>>2 = 1100 -> 1111  //实际上负数是以补码形式保存,这里只是举例
    -8>>>2 = 1100 -> 0011  // 高位补0
    

    在看源码

    i >>> 16 == 0
    // 00000000 00000000 00000000 00000000 
    

    实际上表示的是前16位为0,

     i <<= 16
    

    表示将i左移16位后再赋值给i,整句的意思是如果i前16位为0,则把后16位移动到前16位,并给后16位补0。
    通俗点讲就是,先判断前面16位是不是0,如果是0就把前16位去掉,后面的顶上。
    后续的

    if (i >>> 24 == 0) { n +=  8; i <<=  8; }
    

    i>>>24实际上判断的是8位,之后再判断前4位,再判断前2位

            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;
    

    n就是统计0的数量,但是我们注意到n的初始值为1,按理说不应该是0吗?
    原因在最后一句,如果i>>>31即i的末位是0的话,n应该要再加上1,但是这里实际上是减去了末位的值0,即少加了1,而如果末位的值是1本应该跳过,但这里却多减了1,所以n的初始值不是0而是1.

    n -= i >>> 31;
    也可以看做
    if (i >>> 31 == 1) { n -=  1;  }
    

    至于作者为什么要这样写,不得而知,如果你有什么看法欢迎留言讨论~
    如果把n的初始值设为0,这个方法也可以这么写

    public static int numberOfLeadingZeros(int i) {
            // HD, Figure 5-6
            if (i == 0)
                return 32;
            int n = 0;
            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; }
            if (i >>> 31 == 0) { n += 1;};
            return n;
        }
    

    这种二分判断法方法让原来需要比较32次减少至比较5次,大大提高了效率
    它还有一个姐妹方法,统计从右边开始的0个数,类似就不再叙述了

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

    相关文章

      网友评论

        本文标题:Integer之numberOfLeadingZeros

        本文链接:https://www.haomeiwen.com/subject/bwnbchtx.html