Узнайте количество битов, необходимых для представления положительного целого числа в двоичном коде? - PullRequest
28 голосов
/ 25 марта 2009

Это, вероятно, довольно просто, но чтобы сэкономить мне час или около того горя, кто-нибудь может сказать мне, как вы можете определить количество бит, необходимое для представления заданного положительного целого числа в Java?

например. Я получаю десятичную 11, (1011). Мне нужно получить ответ, 4.

Я подумал, что смогу решить, как установить все биты, кроме самого старшего, в 0, и затем >>> it, я получу свой ответ. Но ... я не могу.

Ответы [ 14 ]

31 голосов
/ 25 марта 2009

Ну, ответ довольно прост. Если у вас есть значение типа int:

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

То же самое существует для Лонга ...

[Изменить] Если проблема заключается в сбрасывании миллисекунд, то Integer.numberOfLeadingZeros (int) достаточно эффективен, но при этом выполняет 15 операций ... Расширяя разумный объем памяти (300 байт, статический), вы можете сократить его до 1-8 операций, в зависимости на диапазоне ваших целых чисел.

27 голосов
/ 25 марта 2009

Ну, вы можете просто посчитать, сколько раз вы сместитесь вправо, прежде чем вы останетесь с нулем:

int value = 11;
int count = 0;
while (value > 0) {
    count++;
    value = value >> 1;
}
24 голосов
/ 25 марта 2009

Моя Java немного ржавая, но не зависящий от языка ответ (если есть функция "log2" и функция "floor") будет:

numberOfBits = floor(log2(decimalNumber))+1

Предполагая, что "decimalNumber" больше 0. Если это 0, вам просто нужен 1 бит.

12 голосов
/ 25 марта 2009

Integer.toBinaryString (номер) .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.

Сначала напишите программу для чтения, затем выясните, где она медленная, а затем сделайте ее быстрее. До и после оптимизации проведите тестирование изменений. Если изменение было недостаточно большим, чтобы сделать код менее читабельным, не беспокойтесь об этом.

11 голосов
/ 25 марта 2009

Взятие двух основанного журнала числа сообщит количество битов, необходимых для его сохранения.

5 голосов
/ 25 марта 2009

Если вы пытаетесь избежать цикла и заботитесь о скорости, вы можете использовать такой метод:

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 (значение <0) немного сомнительно. Отрицательные числа всегда устанавливают самый значимый бит, поэтому, возможно, потребуется полное слово, чтобы представить их. Приспособьте это поведение, если вы заботитесь. </p>

Кстати, чтобы обработать 64-битное целое число, замените строку if (значение <0) этими двумя: </p>

if( value < 0 ) { value = 0; count = 64; }
if( value >= 0x7FFFFFFF ) { value >>= 32; count += 32; }
4 голосов
/ 25 марта 2009

Для неотрицательных значений, вероятно, самый прямой ответ:

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

(Для отрицательных чисел длина битов будет на единицу меньше абсолютного значения, а не бесконечности, которую можно ожидать от двоичной записи дополнения.)

1 голос
/ 22 августа 2018

Двоичный поиск по показателям 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
1 голос
/ 01 сентября 2012

Я хотел бы добавить некоторые другие альтернативы, просто для полноты:

1 BigInteger.valueOf(i).bitLength()

Не очень быстро. Кроме того, BigInteger.bitLength() это ошибка и ненадежность (исправлено в Java7), поскольку, когда требуется более Integer.MAX_VALUE бит (требуется слишком большое количество входных данных !! ) результат переполнения и отрицательные числа появляются для следующих 2^(2*Integer.MAX_VALUE)-2^Integer.MAX_VALUE чисел, которые настолько высоки, что ваша голова может взорваться. Обратите внимание, что по оценкам, Вселенная содержит около 10 ^ 80 атомов; это число 2^4G (G как в Гига, 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);. *1024*

0 голосов
/ 11 февраля 2019

Вы также можете сделать это так, если не хотите изменять исходное значение.

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 справа. Затем мы продолжаем умножать на два, пока i < value. Тем временем мы отслеживаем, сколько битов мы пошли налево.

Так что в этом примере, как только i достигнет 16, значение будет больше 11, и мы остановимся. И тогда мы посчитаем 4 бита: 1 *2 *2 *2 *2 = 16 (=2^4).

Осторожнее со знаковыми числами. При работе со знаковыми числами, которые могут быть как положительными, так и отрицательными, сначала вам нужно будет умножить отрицательные числа на -1. Кроме того, вам нужно подумать, как вы хотите принять во внимание бит знака.

...