Ce code :
System.out.println(Math.abs(Integer.MIN_VALUE));
Renvoie à -2147483648
Ne devrait-il pas retourner la valeur absolue comme 2147483648
?
Ce code :
System.out.println(Math.abs(Integer.MIN_VALUE));
Renvoie à -2147483648
Ne devrait-il pas retourner la valeur absolue comme 2147483648
?
Integer.MIN_VALUE
est -2147483648
mais la plus haute valeur qu'un entier de 32 bits peut contenir est +2147483647
. Tenter de représenter +2147483648
dans un int de 32 bits sera effectivement "reporté" sur l'int de 32 bits. -2147483648
. En effet, lors de l'utilisation d'entiers signés, les représentations binaires en complément à deux de +2147483648
et -2147483648
sont identiques. Ce n'est toutefois pas un problème, car +2147483648
est considéré comme hors de portée.
Pour en savoir un peu plus sur le sujet, vous pouvez consulter l'article suivant Article de Wikipédia sur le complément à deux .
Le comportement que vous indiquez est en effet contre-intuitif. Cependant, ce comportement est celui spécifié par la directive javadoc pour Math.abs(int)
:
Si l'argument n'est pas négatif, l'argument est retourné. Si l'argument est négatif, la négation de l'argument est retournée.
C'est-à-dire, Math.abs(int)
devrait se comporter comme le code Java suivant :
public static int abs(int x){
if (x >= 0) {
return x;
}
return -x;
}
C'est-à-dire, dans le cas négatif, -x
.
Selon le JLS section 15.15.4 le -x
est égal à (~x)+1
où ~
est l'opérateur de complémentation binaire.
Pour vérifier si cela semble correct, prenons l'exemple de -1.
La valeur entière -1
peut être noté comme 0xFFFFFFFF
en hexadécimal en Java (vérifiez cela avec une println
ou toute autre méthode). En prenant -(-1)
donne donc :
-(-1) = (~(0xFFFFFFFF)) + 1 = 0x00000000 + 1 = 0x00000001 = 1
Donc, ça marche.
Essayons maintenant avec Integer.MIN_VALUE
. Sachant que le plus petit entier peut être représenté par 0x80000000
c'est-à-dire que le premier bit est mis à 1 et les 31 bits restants sont mis à 0, nous avons :
-(Integer.MIN_VALUE) = (~(0x80000000)) + 1 = 0x7FFFFFFF + 1
= 0x80000000 = Integer.MIN_VALUE
Et c'est pourquoi Math.abs(Integer.MIN_VALUE)
renvoie à Integer.MIN_VALUE
. Notez également que 0x7FFFFFFF
est Integer.MAX_VALUE
.
Cela dit, comment éviter les problèmes dus à cette valeur de retour contre-intuitive à l'avenir ?
On pourrait, comme l'a souligné @Bombe , lancez notre int
s à long
avant. Cependant, nous devons soit
int
ce qui ne fonctionne pas car Integer.MIN_VALUE == (int) Math.abs((long)Integer.MIN_VALUE)
.long
espérant en quelque sorte que nous n'appellerons jamais Math.abs(long)
avec une valeur égale à Long.MIN_VALUE
puisque nous avons aussi Math.abs(Long.MIN_VALUE) == Long.MIN_VALUE
.Nous pouvons utiliser BigInteger
partout, parce que BigInteger.abs()
renvoie en effet toujours une valeur positive. C'est une bonne alternative, un peu plus lente que la manipulation de types entiers bruts.
Nous pouvons écrire notre propre wrapper pour Math.abs(int)
comme ceci :
/**
En guise de conclusion, ce problème semble être connu depuis un certain temps. Voir par exemple cette entrée sur la règle correspondante de findbugs .
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.