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

Posted

技术标签:

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

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

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

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

【问题讨论】:

【参考方案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

以上是关于找出以二进制表示正整数所需的位数?的主要内容,如果未能解决你的问题,请参考以下文章

一个正整数,如果它能被7整除,或者它的十进制表示法中某个位数上的数字为7,则称其为与7相关的数?

第12章 整数运算

获取Python中整数所需的字节大小

浅谈性能优化之图片压缩加载和格式选择

环形链表(哈希表链表)寻找两个正序数组的中位数(数组二分查找)二进制求和(位运算数学)

读数据压缩入门笔记02_二进制和熵