Ce code:
System.out.println(Math.abs(Integer.MIN_VALUE));
Retour -2147483648
Ne devrait-il pas renvoyer la valeur absolue comme 2147483648?
Ce code:
System.out.println(Math.abs(Integer.MIN_VALUE));
Retour -2147483648
Ne devrait-il pas renvoyer la valeur absolue comme 2147483648?
Réponses:
Integer.MIN_VALUEest -2147483648, mais la valeur la plus élevée qu'un entier 32 bits peut contenir est +2147483647. Tenter de représenter +2147483648dans un int 32 bits "retournera" effectivement à -2147483648. En effet, lorsque vous utilisez des entiers signés, les représentations binaires complémentaires à deux de +2147483648et -2147483648sont identiques. Ce n'est cependant pas un problème, car il +2147483648est considéré comme hors de portée.
Pour un peu plus de lecture à ce sujet, vous voudrez peut-être consulter l'article de Wikipedia sur le complément de Two .
Le comportement que vous indiquez est en effet contre-intuitif. Cependant, ce comportement est celui spécifié par le javadoc pourMath.abs(int) :
Si l'argument n'est pas négatif, l'argument est renvoyé. Si l'argument est négatif, la négation de l'argument est renvoyée.
Autrement dit, Math.abs(int)devrait se comporter comme le code Java suivant:
public static int abs(int x){
if (x >= 0) {
return x;
}
return -x;
}
Autrement dit, dans le cas négatif, -x.
Selon la section JLS 15.15.4 , le -xest égal à (~x)+1, où ~est l'opérateur de complément au niveau du bit.
Pour vérifier si cela vous convient, prenons -1 comme exemple.
La valeur entière -1est peut être notée comme 0xFFFFFFFFen hexadécimal en Java (vérifiez cela avec une printlnou toute autre méthode). Prendre -(-1)donne ainsi:
-(-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 le premier bit mis à 1 et les 31 bits restants mis à 0, nous avons:
-(Integer.MIN_VALUE) = (~(0x80000000)) + 1 = 0x7FFFFFFF + 1
= 0x80000000 = Integer.MIN_VALUE
Et c'est pourquoi Math.abs(Integer.MIN_VALUE)revient Integer.MIN_VALUE. Notez également que 0x7FFFFFFFc'est Integer.MAX_VALUE.
Cela dit, comment pouvons-nous éviter les problèmes dus à cette valeur de retour contre-intuitive à l'avenir?
Nous pourrions, comme l'a souligné @Bombe , lancer nos ints longavant. Cependant, nous devons soit
ints, ce qui ne fonctionne pas car
Integer.MIN_VALUE == (int) Math.abs((long)Integer.MIN_VALUE).longespérant d'une manière ou d'une autre que nous n'appellerons jamais Math.abs(long)avec une valeur égale à Long.MIN_VALUE, puisque nous l'avons également fait Math.abs(Long.MIN_VALUE) == Long.MIN_VALUE.Nous pouvons utiliser BigIntegers partout, car BigInteger.abs()il renvoie en effet toujours une valeur positive. C'est une bonne alternative, bien qu'un peu plus lente que la manipulation de types entiers bruts.
Nous pouvons écrire notre propre wrapper pour Math.abs(int), comme ceci:
/**
* Fail-fast wrapper for {@link Math#abs(int)}
* @param x
* @return the absolute value of x
* @throws ArithmeticException when a negative value would have been returned by {@link Math#abs(int)}
*/
public static int abs(int x) throws ArithmeticException {
if (x == Integer.MIN_VALUE) {
// fail instead of returning Integer.MAX_VALUE
// to prevent the occurrence of incorrect results in later computations
throw new ArithmeticException("Math.abs(Integer.MIN_VALUE)");
}
return Math.abs(x);
}
int positive = value & Integer.MAX_VALUE(débordant essentiellement de Integer.MAX_VALUEà 0au lieu de Integer.MIN_VALUE)Pour finir, ce problème semble connu depuis un certain temps. Voir par exemple cette entrée sur la règle findbugs correspondante .
Pour voir le résultat que vous attendez, effectuez un cast Integer.MIN_VALUEen long:
System.out.println(Math.abs((long) Integer.MIN_VALUE));
Math.absest contre-intuitif en renvoyant un nombre négatif:Math.abs(Long.MIN_VALUE) == Long.MIN_VALUE
ArithmeticException? En outre, le comportement est clairement documenté dans la documentation de l'API.
Math.abs(long). Je m'excuse de mon erreur ici: j'ai pensé que vous aviez proposé l'utilisation de Math.abs(long)comme solution, lorsque vous l'avez montré comme un moyen simple de "voir le résultat attendu par le demandeur". Pardon.
Mais (int) 2147483648L == -2147483648 il y a un nombre négatif qui n'a pas d'équivalent positif donc il n'y a pas de valeur positive pour lui. Vous verrez le même comportement avec Long.MAX_VALUE.
Il y a un correctif à cela dans Java 15 sera une méthode à int et long. Ils seront présents sur les classes
java.lang.Math and java.lang.StrictMath
Les méthodes.
public static int absExact(int a)
public static long absExact(long a)
Si vous réussissez
Integer.MIN_VALUE
OU
Long.MIN_VALUE
Une exception est lancée.
https://bugs.openjdk.java.net/browse/JDK-8241805
Je voudrais voir si Long.MIN_VALUE ou Integer.MIN_VALUE est passé une valeur positive serait return et non une exception mais
Math.abs ne fonctionne pas tout le temps avec de grands nombres J'utilise cette petite logique de code que j'ai apprise quand j'avais 7 ans!
if(Num < 0){
Num = -(Num);
}
sici?
Numégal Integer.MIN_VALUEavant l'extrait de code?