【问题标题】:Find out number of bits needed to represent a positive integer in binary?找出以二进制表示正整数所需的位数?
【发布时间】:2010-10-15 08:46:58
【问题描述】:

这可能是非常基本的,但是为了节省我一个小时左右的悲伤,谁能告诉我如何计算出在 Java 中表示给定正整数所需的位数?

例如我得到一个小数点 11,(1011)。我需要得到答案,4。

我想如果我能弄清楚如何将除最高有效位以外的所有位设置为 0,然后 >>> 它,我会得到我的答案。但是……我不能。

【问题讨论】:

    标签: java bit-manipulation bits


    【解决方案1】:

    嗯,答案很简单。如果你有一个 int 值:

    int log2(int value) {
        return Integer.SIZE-Integer.numberOfLeadingZeros(value);
    }
    

    Long 也是如此……

    [编辑] 如果剃须毫秒是这里的问题,则 Integer.numberOfLeadingZeros(int) 相当有效,但仍执行 15 次操作...扩展合理数量的内存(300 字节,静态)您可以将其减少到 1 到 8 次操作之间,具体取决于在你的整数范围内。

    【讨论】:

    • 这是最快的解决方案。而且比公认的答案更容易理解!
    • 这可能是最快的解决方案,但从技术上讲它并非万无一失。尝试用 value = 0 调用它,结果是:0。这是错误的,原因有两个:首先,从数学上讲,log2(0) 是未定义的。其次,在原始问题的上下文中:当您想要存储一个值为 0 的整数时,您至少需要一位。
    • 如果这是唯一的问题,它可以是特殊情况,并且仍然比其他答案更容易理解和更高效。
    • 脱离javadoc:注意这个方法与以2为底的对数密切相关。对于所有正的int值x:floor(log2(x)) = 31 - numberOfLeadingZeros(x)ceil(log2(x)) = 32 - numberOfLeadingZeros(x - 1)
    【解决方案2】:

    嗯,你可以数一数在你只剩下零之前你向右移动了多少次:

    int value = 11;
    int count = 0;
    while (value > 0) {
        count++;
        value = value >> 1;
    }
    

    【讨论】:

    • 天啊!是的,这很简单。我期待一些很棒的小魔法......感谢您的快速回复,我现在会使用它,但我很想看看是否有任何没有循环的方法等等。
    • 好吧,你可以展开循环,因为它应该以 32 次迭代为界(或 64 次——但是 Java 可以工作)。
    • int 在 Java 中是 32 位,long 是 64。
    • 好的,我给你发了一个没有循环的方法。不过,它仍然需要几个步骤;)。
    • 对底片不太好。试试while (value != 0) { ++count; value >>>= 1; }。 >>> 是逻辑(无符号扩展)右移运算符。
    【解决方案3】:

    我的 Java 有点生疏,但与语言无关的答案(如果有可用的“log2”函数和“floor”函数)将是:

    numberOfBits = floor(log2(decimalNumber))+1
    

    假设“decimalNumber”大于0。如果为0,则只需要1位。

    【讨论】:

    • 我认为decimalNumber应该是decimalNumber + 1。log_2 256是8,而它需要9位来表示。 log_2 0 未定义,但需要零位来表示。
    • @strager:我认为你很接近。我需要使用“地板”而不是“天花板”,然后添加 +1。显然,首先需要检查“decimalNumber == 0”。例如,试试 255(应该是 8)。
    • @gnovice,啊,很好。我自己也不确定。感谢您调查它。 =]
    • 它当然不适用于负整数,有时您还必须计算那些位数:) 但是,如果您正在压缩数据,那么我认为更好的方法是存储位表示符号,然后存储它的绝对值,因为 -1 将占用 32 位,而 1 将占用 2(1 表示 1,1 表示符号)。
    • @Statement:你说的有道理,但 OP 说他们只是想获得正整数的位数。
    【解决方案4】:

    Integer.toBinaryString(number).length();

    天哪……为什么投反对票?

    public class Main
    {
        public static void main(final String[] argv)
        {
            System.out.println(Integer.toBinaryString(0).length());
            System.out.println(Integer.toBinaryString(1).length());
            System.out.println(Integer.toBinaryString(2).length());
            System.out.println(Integer.toBinaryString(3).length());
            System.out.println(Integer.toBinaryString(4).length());
            System.out.println(Integer.toBinaryString(5).length());
            System.out.println(Integer.toBinaryString(6).length());
            System.out.println(Integer.toBinaryString(7).length());
            System.out.println(Integer.toBinaryString(8).length());
            System.out.println(Integer.toBinaryString(9).length());
        }
    }
    

    输出:

    1
    1
    2
    2
    3
    3
    3
    3
    4
    4
    

    以下是对各种解决方案速度的简单测试:

    public class Tester 
    {
        public static void main(final String[] argv) 
        {
            final int size;
            final long totalA;
            final long totalB;
            final long totalC;
            final long totalD;
    
            size = 100000000;
    
            totalA = test(new A(), size);
            totalB = test(new B(), size);
            totalC = test(new C(), size);
            totalD = test(new D(), size);
    
            System.out.println();
            System.out.println("Total D = " + totalD + " ms");
            System.out.println("Total B = " + totalB + " ms");
            System.out.println("Total C = " + totalC + " ms");
            System.out.println("Total A = " + totalA + " ms");
    
            System.out.println();
            System.out.println("Total B = " + (totalB / totalD) + " times slower");
            System.out.println("Total C = " + (totalC / totalD) + " times slower");
            System.out.println("Total A = " + (totalA / totalD) + " times slower");
        }
    
        private static long test(final Testable tester, 
                                 final int      size)
        {
            final long start;
            final long end;
            final long total;
    
            start = System.nanoTime();
            tester.test(size);
            end   = System.nanoTime();
            total = end - start;
    
            return (total / 1000000);
        }
    
        private static interface Testable
        {
            void test(int size);
        }
    
        private static class A
            implements Testable
        {
            @Override
            public void test(final int size)
            {
                int value;
    
                value = 0;
    
                for(int i = 1; i < size; i++)
                {
                    value += Integer.toBinaryString(i).length();
                }
    
                System.out.println("value = " + value);
            }    
        }
    
        private static class B
            implements Testable
        {
            @Override
            public void test(final int size)
            {
                int total;
    
                total = 0;
    
                for(int i = 1; i < size; i++)
                {
                    int value = i;
                    int count = 0;
    
                    while (value > 0) 
                    {
                        count++;
                        value >>= 1;
                    }
    
                    total += count;
                }
    
                System.out.println("total = " + total);
            }    
        }
    
        private static class C
            implements Testable
        {
            @Override
            public void test(final int size)
            {
                int total;
                final double log2;
    
                total = 0;
                log2  = Math.log(2);
    
                for(int i = 1; i < size; i++)
                {
                    final double logX;
                    final double temp;
    
                    logX   = Math.log(i);
                    temp   = logX / log2;                
                    total += (int)Math.floor(temp) + 1;
                }
    
                System.out.println("total = " + total);
            }    
        }
    
        private static class D
            implements Testable
        {
            @Override
            public void test(final int size)
            {
                int total;
    
                total = 0;
    
                for(int i = 1; i < size; i++)
                {
                    total += 32-Integer.numberOfLeadingZeros(i);
                }
    
                System.out.println("total = " + total);
            }    
        }
    }
    

    我机器上的输出是:

    value = -1729185023
    total = -1729185023
    total = -1729185023
    total = -1729185023
    
    Total D = 118 ms
    Total B = 1722 ms
    Total C = 4462 ms
    Total A = 5704 ms
    
    Total B = 14 times slower
    Total C = 37 times slower
    Total A = 48 times slower
    

    对于那些抱怨速度的人...https://en.wikipedia.org/wiki/Program_optimization#Quotes.

    先把程序写成可读的,再找出慢的地方,再让它变快。优化前后测试变化。如果更改的幅度不足以降低代码的可读性,请不要为更改而烦恼。

    【讨论】:

    • 您可能投了反对票,因为您的解决方案非常昂贵。
    • 没有要求它快:-)
    • 看起来 100,000,000(在我的桌面上)可能不会成为“真实”程序的瓶颈。
    • 非常好的基准!为了完整起见,您可以添加BigInteger.valueOf(i).bitLength()(这是慢的一面:在我的机器上,大约比您的 D 慢 5 或 6 倍)
    • 但是,BigInteger.bitLength() 存在漏洞且不可靠(至少在 Java 6 中)。 bugs.sun.com/bugdatabase/…
    【解决方案5】:

    取两个基数的日志将报告存储它所需的位数。

    【讨论】:

    • A) -2 rep 不会杀了你 B) 这可能是在审计中,对于审计的主题有点模棱两可,因此被否决了,所以它不会再叮嘱某人了。
    • 所以我猜是int(log2(n)) + 1
    【解决方案6】:

    如果你想避免循环并且你关心速度,你可以使用这样的方法:

    int value = ...;
    int count = 0;
    if( value < 0 ) { value = 0; count = 32; }
    if( value >= 0x7FFF ) { value >>= 16; count += 16; }
    if( value >= 0x7F ) { value >>= 8; count += 8; }
    if( value >= 0x7 ) { value >>= 4; count += 4; }
    if( value >= 0x3 ) { value >>= 2; count += 2; }
    if( value >= 0x1 ) { value >>= 1; count += 1; }
    

    Java 没有无符号整数,所以首先 if( value

    顺便说一句,要处理 64 位整数,请将 if( value

    if( value < 0 ) { value = 0; count = 64; }
    if( value >= 0x7FFFFFFF ) { value >>= 32; count += 32; }
    

    【讨论】:

    • 这给出了错误的结果。对于 value = 4,当它应该是 3 时返回 2。事实上它根本不会输出 3,它在 value =8 处直接跳到 4。
    • 我很抱歉。 > 符号应该是 >= 符号。我相信它现在应该可以工作了。
    【解决方案7】:

    对于非负值,可能最直接的答案是:

    java.math.BigDecimal.valueOf(value).bitLength()
    

    (对于负数,它会给出比绝对值小一的位长度,而不是您从二进制补码表示法中期望的无穷大。)

    【讨论】:

    • 不是真的绝对值的位长System.out.println(BigInteger.valueOf(-1).bitLength()); 打印的是 0,而不是 1
    • @UnaiVivi 嗯,是的。已更正。如果该方法将IllegalStateException 用于负值而不是做一些奇怪的事情,可能会更好。
    • 您知道他们为什么这样做(对于负数)吗?我看不出他们这样做的任何用处......
    • @UnaiVivi 我相信如果你加一个,你会得到用二进制补码表示值所需的最小位数。
    【解决方案8】:

    为了完整起见,我想添加一些其他替代方案:

    1 BigInteger.valueOf(i).bitLength()

    不是很快。此外,BigInteger.bitLength() 存在漏洞且不可靠(在 Java7 中已修复),因为当需要超过 Integer.MAX_VALUE 位时(需要非常高的输入数!![例如左移 1 次Integer.MAX_VALUE 次,又名2^Integer.MAX_VALUE] ) 结果溢出,下一个 2^(2*Integer.MAX_VALUE)-2^Integer.MAX_VALUE 数字出现负数,这个数字太高了,你的脑袋可能会爆炸。请注意,据估计宇宙包含大约 10^80 个原子;这个数字是2^4GG 在 Giga 中,1024*1024*1024)。

    2

    static int neededBits(int i)
    {
        assert i > 0;
        int res;
        int sh;
        res = ((i > 0xFFFF) ? 1 : 0) << 4;
        i >>= res;
        sh = ((i > 0xFF) ? 1 : 0) << 3;
        i >>= sh;
        res |= sh;
        sh = ((i > 0xF) ? 1 : 0) << 2;
        i >>= sh;
        res |= sh;
        sh = ((i > 0x3) ? 1 : 0) << 1;
        i >>= sh;
        res |= sh;
        res |= (i >> 1);
        return res + 1;
    }
    

    一个非常快的解决方案,但仍然是你的一半快32 - Integer.numberOfLeadingZeros(i);

    【讨论】:

      【解决方案9】:

      对 2 的指数进行二进制搜索比位移 (top voted answer) 解决方案更快,如果数字很大(数千个十进制数字),这可能很有价值,您知道最大可用位并且您不知道想要生成表格:

          int  minExpVal   = 0;
          int  maxExpVal   = 62;
          int  medExpVal   = maxExpVal >> 1;
          long medianValue = 0l;
      
          while (maxExpVal - minExpVal > 1) {
              medianValue = 1l << medExpVal;
              if (value > medianValue) {
                  minExpVal = medExpVal;
              } else {
                  maxExpVal = medExpVal;
              }
              medExpVal = (minExpVal + maxExpVal) >> 1;
          }
      
          return value == 1l << maxExpVal ?  maxExpVal  + 1 : maxExpVal;
      

      但是,使用前导零的解决方案仍然要快得多:

      return Long.SIZE - Long.numberOfLeadingZeros(value);
      

      基准测试:

      Leading zeros time is: 2 ms
      BinarySearch time is: 95 ms
      BitShift time is: 135 ms
      

      【讨论】:

        【解决方案10】:

        这个对我有用!

        int numberOfBitsRequired(int n)
        {
            return (int)Math.floor(Math.log(n)/Math.log(2)) + 1;
        }
        

        要包含负数,您可以添加一个额外的位并使用它来指定符号。

        public static int numberOfBitsRequiredSigned(int n)
        {
            return (int)Math.floor(Math.log(Math.abs(n))/Math.log(2)) + 2;
        }
        

        【讨论】:

          【解决方案11】:

          如果不想修改原来的值,也可以这样操作。

          unsigned int value = 11;
          unsigned int count = 0;
          if(value > 0)
          {
              for(int i=1;i<value;i*=2) // multiply by two => shift one to left
              {
                  ++count;
              }
          }
          

          注意:让编译器担心将i*=2 转换为位移操作以提高性能。

          对于我们中间的视觉思想家:

          64 32 16  8  4  2  1
           0  0  0  1  0  1  1  -> binary representation of decimal number 'value' = 11 (=1+2+8)
          

          我们从右边的i=1 开始。 然后我们继续乘以 2,直到 i &lt; value。 同时,我们会记录我们向左移动了多少位。

          所以在这个例子中,一旦i 达到 16,值就大于 11,因此我们停止。然后我们将计算 4 位:1 *2 *2 *2 *2 = 16 (=2^4)

          小心有符号数。在处理可能是正数或负数的有符号数时,您首先必须将负数乘以 -1。此外,您还必须考虑如何考虑符号位。

          【讨论】:

            【解决方案12】:

            这是用 C 语言编写的,但我怀疑您可以相当容易地转换为 Java:

            Find the log base 2 of an N-bit integer in O(lg(N)) operations

            【讨论】:

              【解决方案13】:

              这样的事情怎么样:

              public static int getNumberOfBits(int N) {
                  int bits = 0;
                      while(Math.pow(2, bits) <= N){
                         bits++;
                     }
                     return bits;
              }
              

              我知道您正在寻找一种不使用循环的方法,但我觉得这很困难,否则因为位只是数字的 2 次方。

              【讨论】:

                【解决方案14】:
                (int) Math.ceil((Math.log(n) / Math.log(2))
                

                当然这只适用于正整数。

                【讨论】:

                • 这给出了 n = 128 的错误结果。它应该是 8 时输出 7
                猜你喜欢
                • 2018-03-17
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2021-10-01
                • 2015-01-21
                • 1970-01-01
                • 2012-11-26
                • 2019-02-04
                相关资源
                最近更新 更多