web-dev-qa-db-fra.com

Découvrez le nombre de bits nécessaires pour représenter un entier positif en binaire?

C'est probablement assez basique, mais pour me sauver une heure de chagrin, quelqu'un peut-il me dire comment calculer le nombre de bits requis pour représenter un entier positif donné en Java?

par exemple. Je reçois un nombre décimal 11 (1011). Je dois obtenir la réponse, 4.

Je pensais que si je pouvais trouver un moyen de définir tous les bits autres que le plus significatif à 0, puis >>> je recevrais ma réponse. Mais ... je ne peux pas.

26
joinJpegs

Eh bien, vous pouvez simplement compter combien de fois vous passez juste avant qu'il ne vous reste que zéro:

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

Eh bien, la réponse est assez simple. Si vous avez une valeur int:

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

La même chose existe pour Long ...

[Éditer] Si le rasage en millisecondes est un problème ici, Integer.numberOfLeadingZeros (int) est raisonnablement efficace, mais effectue tout de même 15 opérations ... En développant une quantité de mémoire raisonnable (300 octets, statique), vous pourriez le raser entre 1 et 8 opérations, en fonction de la plage de vos entiers.

31
Varkhan

Mon Java est un peu rouillé, mais la réponse indépendante de la langue (s'il existe une fonction "log2" et une fonction "étage" disponible) serait la suivante:

numberOfBits = floor(log2(decimalNumber))+1

En supposant que "decimalNumber" est supérieur à 0. Si la valeur est 0, vous avez simplement besoin de 1 bit.

23
gnovice

Integer.toBinaryString (nombre) .length ();

Bon chagrin ... pourquoi les votes négatifs?

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());
    }
}

Sortie:

1
1
2
2
3
3
3
3
4
4

Voici un test simple pour la rapidité des différentes solutions:

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);
        }    
    }
}

La sortie sur ma machine est:

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

Pour ceux d’entre vous qui se plaignent de la vitesse ... https://en.wikipedia.org/wiki/Program_optimization#Quotes .

Écrivez le programme pour qu'il soit lisible en premier, puis trouvez où il est lent, puis accélérez-le. Avant et après l'optimisation, testez le changement. Si le changement n'était pas assez important pour permettre de rendre le code moins lisible, ne vous embêtez pas avec le changement.

12
TofuBeer

En prenant le journal à deux bases du nombre, on indiquera le nombre de bits requis pour le stocker. 

11
ojblass

Si vous essayez d'éviter une boucle et que vous vous souciez de la vitesse, vous pouvez utiliser une méthode comme celle-ci:

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 n'a pas d'entiers non signés, donc d'abord si (valeur <0) est un peu discutable. Les nombres négatifs définissent toujours le bit le plus significatif, il faut donc sans doute utiliser le mot entier pour les représenter. Adaptez ce comportement si vous vous en souciez.

Incidemment, pour gérer un entier de 64 bits, remplacez la ligne if (valeur <0) par ces deux:

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

Pour les valeurs non négatives, la réponse la plus directe est probablement la suivante:

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

(Pour les nombres négatifs, la longueur en bits sera inférieure de un à la valeur absolue, plutôt qu'à l'infini que vous attendez de la notation du complément à deux.)

4

Je voudrais ajouter quelques alternatives, juste pour être complet:

1 BigInteger.valueOf(i).bitLength()

Pas très vite. De plus, BigInteger.bitLength() est bogué et peu fiable (corrigé dans Java7), puisqu’il faut plus que Integer.MAX_VALUE bits (un nombre extrêmement élevé d’entrées est nécessaire !! [comme 1 Integer.MAX_VALUE décalé à gauche, par exemple 2^Integer.MAX_VALUE]), le résultat dépasse et les nombres négatifs apparaissent pour les numéros 2^(2*Integer.MAX_VALUE)-2^Integer.MAX_VALUE suivants, qui sont si élevés que votre tête pourrait exploser. Notez que l’on estime que l’univers contient environ 10 à 80 atomes; ce nombre est 2^4G (G comme dans 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;
}

Une solution très rapide, mais toujours moitié moins rapide que la vieille 32 - Integer.numberOfLeadingZeros(i);.

1
Unai Vivi

Ceci est en C, mais je suppose que vous pourriez convertir assez facilement en Java:

Recherche la base de journal 2 d'un entier N bits dans O(lg(N)) opérations

0
Jim Mischel

Vous pouvez également le faire comme ceci, si vous ne souhaitez pas modifier la valeur d'origine.

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;
    }
}

Remarque: Laissez le compilateur s’inquiéter de la possibilité de transformer i*=2 en une opération de décalage de bit pour améliorer les performances.

Pour les penseurs visuels parmi nous:

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

Nous commençons avec i=1 à droite. Ensuite, nous continuons à multiplier par deux, aussi longtemps que i < value. En attendant, nous gardons une trace du nombre de bits que nous sommes allés à gauche.

Ainsi, dans cet exemple, dès que i atteint 16, la valeur est supérieure à 11 et nous arrêtons donc. Et nous aurons alors compté 4 bits: 1 *2 *2 *2 *2 = 16 (=2^4).

Attention aux numéros signés. Lorsque vous utilisez des nombres signés qui peuvent être positifs ou négatifs, vous devez d’abord multiplier les nombres négatifs par -1. De plus, vous devrez considérer comment vous souhaitez prendre en compte le bit de signe.

0
Yeti

Qu'en est-il quelque chose comme ça:

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

Je sais que vous cherchez un moyen de ne pas utiliser les boucles, mais j'estime que c'est assez stricts, sinon les bits ne sont que deux à la puissance d'un nombre.

0
Doogles

Celui-ci fonctionne pour moi!

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

Pour inclure également des nombres négatifs, vous pouvez ajouter un bit supplémentaire et l'utiliser pour spécifier le signe.

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

La recherche binaire sur les exposants de 2 est plus rapide que la solution de décalage de bits ( réponse votée en haut ), ce qui peut être utile si les nombres sont énormes (des milliers de chiffres décimaux), vous connaissez le nombre maximal de bits disponibles et vous pas envie de générer les tables:

    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;

Cependant, la solution utilisant les zéros à gauche serait encore beaucoup plus rapide:

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

Benchmarks:

Leading zeros time is: 2 ms
BinarySearch time is: 95 ms
BitShift time is: 135 ms
0
Ilya K.
(int) Math.ceil((Math.log(n) / Math.log(2))

Bien sûr, cela ne fonctionne que pour les entiers positifs.

0
Merzbow