JDK源码之Integer类——numberOfLeadingZeros()方法

Posted 二木成林

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JDK源码之Integer类——numberOfLeadingZeros()方法相关的知识,希望对你有一定的参考价值。

方法功能:获取指定整数转换成二进制后的前导零的个数。

比如数字3的二进制是0000 0000 0000 0000 0000 0000 0000 0011,那么可以得到它的前置0有30位,最后两位是11。

该方法的源码如下:

    /**
     * Returns the number of zero bits preceding the highest-order
     * ("leftmost") one-bit in the two's complement binary representation
     * of the specified {@code int} value.  Returns 32 if the
     * specified value has no one-bits in its two's complement representation,
     * in other words if it is equal to zero.
     *
     * <p>Note that this method is closely related to the logarithm base 2.
     * For all positive {@code int} values x:
     * <ul>
     * <li>floor(log<sub>2</sub>(x)) = {@code 31 - numberOfLeadingZeros(x)}
     * <li>ceil(log<sub>2</sub>(x)) = {@code 32 - numberOfLeadingZeros(x - 1)}
     * </ul>
     *
     * @param i the value whose number of leading zeros is to be computed
     * @return the number of zero bits preceding the highest-order
     *     ("leftmost") one-bit in the two's complement binary representation
     *     of the specified {@code int} value, or 32 if the value
     *     is equal to zero.
     * @since 1.5
     */
    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;
    }

对该方法进行注释:

    /**
     * 回指定int值的二进制补码二进制表示形式中最高位(“最左端”)一位之前的零位数目。 如果指定值的二进制补码表示中没有一位(即等于零),则返回32。
     * 注意,此方法是密切相关的数底2对所有正int值x:
     * floor(log 2 (x))= 31 - numberOfLeadingZeros(x)
     * ceil(log 2 (x))= 32 - numberOfLeadingZeros(x - 1)
     * 比如数字3的二进制是0000 0000 0000 0000 0000 0000 0000 0011,那么可以得到它的前置0有30位,最后两位是11
     * 比如数字8的二进制是0000 0000 0000 0000 0000 0000 0000 1000,那么可以得到它的前置0有28位,最后四位是1000
     * 参考链接:https://blog.csdn.net/cnds123321/article/details/117338989
     *
     * @param i 要计算前导零个数的值
     * @return 指定的int值的二进制补码二进制表示形式中("最左端")一位之前的零位数目;如果该值等于0,则为32位
     */
    public static int numberOfLeadingZeros(int i) {
        // 如果传入的数是0,那么32位二进制一定都是0,所以直接返回32即可,不需要再进行下面的操作
        if (i == 0)
            return 32;
        // 局部变量,统计前导零的个数,初始值为1,即至少存在1个,因为0个情况已经被上面的if判断排除了
        int n = 1;
        // 这个思路就是二分查找,首先把32位的数分为高低16位,如果非零值位于高16位,后续再将高16位继续二分为高低8位,一直二分到集合中只有1个元素
        /*
            将i无符号右移16位后,有二种情况:
                第一种情况,i=0,则第一个非零值位于低16位,i至少有16个前导0,同时将i左移16位(把低16位移到原高16位的位置,这样情况1和情况2就能统一后续的判断方式)
                    例如:数字123的二进制是0000 0000 0000 0000  0000 0000 0111 1011,无符号右移16位后二进制是0000 0000 0000 0000  0000 0000 0000 0000,表明第一个非零值在低16位
                第二种情况,i!=0,则第一个非零值位于高16位,后续在高16位中继续判断
         */
        // 只是判断当i无符号右移16位后是否等于0,如果等于0则表示第一个非零值在低16位,如果不等于则表示第一个非零值在高16位
        if (i >>> 16 == 0) {
            // 执行到这里,表示第一个非零值在低16位,那么表示数字i的高16位全是0,也就是说至少有16个前导0,用n记录起来
            n += 16;
            // 既然i的第一个非零值在低16位,那么将i左移16位,将低16位移动到原来高16位的位置,继续判断
            // 例如:数字123的二进制是0000 0000 0000 0000  0000 0000 0111 1011,左移16位后二进制是0000 0000 0111 1011  0000 0000 0000 0000
            i <<= 16;
        }
        // 判断第一个非零值是否在高8位,如果i>>>24==0为true表示第一个非零值在低8位,为false表示在高8位
        if (i >>> 24 == 0) {
            // 执行到这里,表示第一个非零值在低8位,那么表示数字i的高8位全是0,也就是说至少有8个前导0,用n记录起来
            n += 8;
            // 既然i的第一个非零值在低8位,那么将i左移8位,将低8位移动到原来高8位的位置,继续判断
            // 例如:数字123的二进制是0000 0000 0000 0000  0000 0000 0111 1011
            // 左移16位后二进制是    0000 0000 0111 1011  0000 0000 0000 0000
            // 又左移8位后二进制是   0111 1011 0000 0000  0000 0000 0000 0000
            i <<= 8;
        }
        // 判断第一个非零值是否在高4位,如果i>>>28==0为true表示第一个非零值在低4位,为false表示在高4位
        if (i >>> 28 == 0) {
            // 执行到这里,表示第一个非零值在低4位,那么表示数字i的高4位全是0,也就是说至少有4个前导0,用n记录起来
            n += 4;
            // 既然i的第一个非零值在低4位,那么将i左移4位,将低4位移动到原来高4位的位置,继续判断
            // 例如:数字1的二进制是0000 0000 0000 0000  0000 0000 0000 0001
            // 左移16位后二进制是  0000 0000 0000 0001  0000 0000 0000 0000
            // 又左移8位后二进制是 0000 0001 0000 0000  0000 0000 0000 0000
            // 又左移4位后二进制是 0001 0000 0000 0000  0000 0000 0000 0000
            i <<= 4;
        }
        // 判断第一个非零值是否在高2位,如果i>>>30==0为true表示第一个非零值在低2位,为false表示在高2位
        if (i >>> 30 == 0) {
            // 执行到这里,表示第一个非零值在低2位,那么表示数字i的高2位全是0,也就是说至少有2个前导0,用n记录起来
            n += 2;
            // 既然i的第一个非零值在低2位,那么将i左移2位,将低2位移动到原来高2位的位置,继续判断
            // 例如:数字1的二进制是0000 0000 0000 0000  0000 0000 0000 0001
            // 左移16位后二进制是  0000 0000 0000 0001  0000 0000 0000 0000
            // 又左移8位后二进制是 0000 0001 0000 0000  0000 0000 0000 0000
            // 又左移4位后二进制是 0001 0000 0000 0000  0000 0000 0000 0000
            // 又左移2位后二进制是 0100 0000 0000 0000  0000 0000 0000 0000
            i <<= 2;
        }
        // i >>> 31是判断第一个非零值是否在高1位,如果在高1位则i>>>31得到的结果是1,如果在低1位则i>>>31得到的结果是0
        // n由于是从1开始的,所以这里需要减,如果是从0开始,就需要加
        n -= i >>> 31;
        // 返回统计的前导零个数
        return n;
    }

其实这个方法就是利用位运算求前导零的个数,思路就是不断进行二分来记录统计前导零的出现次数

画了一个图,可能方便理解些:

以上是关于JDK源码之Integer类——numberOfLeadingZeros()方法的主要内容,如果未能解决你的问题,请参考以下文章

JDK源码之Integer类——rotateRight()方法

JDK源码之Integer类——rotateLeft()方法

JDK源码之Integer类——numberOfLeadingZeros()方法

JDK源码之Integer类——numberOfTrailingZeros()方法

JDK源码之Integer类—signum()方法

JDK源码之Integer类—reverseBytes()方法