找出表示二进制正整数所需的位数?

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

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

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

好吧,你可以算一下你在离开前只有零次移动的次数:

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

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

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

Long存在同样的情况……

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

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

 numberOfBits = floor(log2(decimalNumber))+1 

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

Integer.toBinaryString(数字)。长度();

好悲伤…为什么下来投票?

 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

编写程序首先可读,然后找出它慢的地方,然后加快速度。 优化测试之前和之后的变化。 如果更改不足以使代码不易读取的费用,请不要理会更改。

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

如果你试图避免循环而你关心速度,你可以使用这样的方法:

 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 <0)有点可疑。 负数始终设置最重要的位,因此可以说需要完整的单词来表示它们。 如果你在意的话,适应这种行为。

顺便提一下,要处理64位整数,请用这两个替换if(value <0)行:

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

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

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

(对于负数,它将给出比绝对值小1的位长度,而不是你期望从2的补码表示法得到的无穷大。)

我想补充一些其他选择,只是为了完整起见:

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个primefaces; 这个数字是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);快速解决方案32 - Integer.numberOfLeadingZeros(i);

这是在C中,但我怀疑你可以很容易地转换为Java:

在O(lg(N))运算中找到N位整数的对数库2

 (int) Math.ceil((Math.log(n) / Math.log(2)) 

当然这仅适用于正整数。

这样的事情怎么样:

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

我知道你正在寻找一种不使用循环的方法,但我觉得这是相当紧张的前提,否则比特只是数字的两倍。

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

  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