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.
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;
}
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.
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.
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.
En prenant le journal à deux bases du nombre, on indiquera le nombre de bits requis pour le stocker.
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; }
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.)
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);
.
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
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.
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.
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;
}
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
(int) Math.ceil((Math.log(n) / Math.log(2))
Bien sûr, cela ne fonctionne que pour les entiers positifs.