这可能是非常基本的,但为了节省我一小时左右的悲伤,任何人都可以告诉我如何计算出代表Java中给定正整数所需的位数?
e.g。我得到小数11,(1011)。我需要得到答案,4。
我想我是否可以解决如何将除最高位之外的所有位设置为0,然后>>>它,我得到了答案。但是......我做不到。
答案 0 :(得分:31)
嗯,答案很简单。如果你有一个int值:
int log2(int value) {
return Integer.SIZE-Integer.numberOfLeadingZeros(value);
}
Long的存在同样......
[编辑] 如果剃须毫秒是一个问题,Integer.numberOfLeadingZeros(int)是相当有效的,但仍然做15次操作......扩展合理的内存量(300字节,静态),你可以将其削减到1到8个操作,具体取决于在你的整数范围内。
答案 1 :(得分:27)
嗯,你可以算一下你在离开前只有零次移动的次数:
int value = 11;
int count = 0;
while (value > 0) {
count++;
value = value >> 1;
}
答案 2 :(得分:24)
我的Java有点生疏,但与语言无关的答案(如果有“log2”功能和“floor”功能可用)将是:
numberOfBits = floor(log2(decimalNumber))+1
假设“decimalNumber”大于0.如果为0,则只需要1位。
答案 3 :(得分:12)
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。
首先将程序编写为可读,然后找出它慢的位置,然后加快速度。优化测试之前和之后的变化。如果更改不够大,不足以使代码不易读取,请不要为更改而烦恼。
答案 4 :(得分:11)
根据该数字的两个基数记录将报告存储它所需的位数。
答案 5 :(得分:5)
如果你试图避免循环而你关心速度,你可以使用这样的方法:
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&lt; 0)有点可疑。负数总是设置最重要的位,所以可以说需要完整的单词来表示它们。如果你在意,请调整这种行为。
顺便说一句,要处理64位整数,请将if(value&lt; 0)行替换为这两行:
if( value < 0 ) { value = 0; count = 64; }
if( value >= 0x7FFFFFFF ) { value >>= 32; count += 32; }
答案 6 :(得分:4)
对于非负值,可能最直接的答案是:
java.math.BigDecimal.valueOf(value).bitLength()
(对于负数,它将给出比绝对值小1的位长度,而不是你对2的补码表示法所期望的无穷大。)
答案 7 :(得分:1)
我想补充一些其他选择,只是为了完整起见:
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^4G
(G
与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);
的一半。
答案 8 :(得分:1)
在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
答案 9 :(得分:0)
这是在C中,但我怀疑你可以很容易地转换为Java:
Find the log base 2 of an N-bit integer in O(lg(N)) operations
答案 10 :(得分:0)
这样的事情:
public static int getNumberOfBits(int N) {
int bits = 0;
while(Math.pow(2, bits) <= N){
bits++;
}
return bits;
}
我知道你正在寻找一种不使用循环的方法,但我觉得这是相当紧张的前提,否则因为位数只是数字的两倍。
答案 11 :(得分:0)
这个对我有用!
cal
要同时包含负数,可以添加一个额外的位,并使用它来指定符号。
int numberOfBitsRequired(int n)
{
return (int)Math.floor(Math.log(n)/Math.log(2)) + 1;
}
答案 12 :(得分:0)
如果您不想修改原始值,也可以这样做。
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 < value
。
同时,我们跟踪到左边有多少位。
因此,在此示例中,i
一旦达到16,该值就会大于11,因此我们停止了。然后,我们将计算出4位:1 *2 *2 *2 *2 = 16 (=2^4)
。
注意带符号的数字。处理带正负号的带符号数字时,首先必须将负数字乘以-1。此外,您还必须考虑如何考虑符号位。
答案 13 :(得分:-1)
(int) Math.ceil((Math.log(n) / Math.log(2))
当然这只适用于正整数。