Dans le programme suivant, vous pouvez voir que chaque valeur légèrement inférieure à .5
est arrondie à l'exception de 0.5
.
for (int i = 10; i >= 0; i--) {
long l = Double.doubleToLongBits(i + 0.5);
double x;
do {
x = Double.longBitsToDouble(l);
System.out.println(x + " rounded is " + Math.round(x));
l--;
} while (Math.round(x) > i);
}
impressions
10.5 rounded is 11
10.499999999999998 rounded is 10
9.5 rounded is 10
9.499999999999998 rounded is 9
8.5 rounded is 9
8.499999999999998 rounded is 8
7.5 rounded is 8
7.499999999999999 rounded is 7
6.5 rounded is 7
6.499999999999999 rounded is 6
5.5 rounded is 6
5.499999999999999 rounded is 5
4.5 rounded is 5
4.499999999999999 rounded is 4
3.5 rounded is 4
3.4999999999999996 rounded is 3
2.5 rounded is 3
2.4999999999999996 rounded is 2
1.5 rounded is 2
1.4999999999999998 rounded is 1
0.5 rounded is 1
0.49999999999999994 rounded is 1
0.4999999999999999 rounded is 0
J'utilise Java 6 mise à jour 31.
java
floating-point
double
rounding
Peter Lawrey
la source
la source
0.5
au nombre puis en utilisantfloor
; Java 7 ne le documente plus de cette façon (probablement / espérons-le parce qu'ils l'ont corrigé).Réponses:
Sommaire
En Java 6 (et probablement plus tôt),
round(x)
est implémenté en tant quefloor(x+0.5)
. 1 Il s'agit d'un bug de spécification, précisément pour ce cas pathologique. 2 Java 7 ne rend plus obligatoire cette implémentation défectueuse. 3Le problème
0,5 + 0,49999999999999994 est exactement 1 en double précision:
C'est parce que 0.4999999999999999994 a un exposant plus petit que 0.5, donc quand ils sont ajoutés, sa mantisse est décalée et l'ULP devient plus grand.
La solution
Depuis Java 7, OpenJDK (par exemple) l'implémente ainsi: 4
1. http://docs.oracle.com/javase/6/docs/api/java/lang/Math.html#round%28double%29
2. http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6430675 (crédits à @SimonNickerson pour l'avoir trouvé)
3. http://docs.oracle.com/javase/7/docs/api/java/lang/Math.html#round%28double%29
4. http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/7u40-b43/java/lang/Math.java#Math.round%28double%29
la source
round
dans le Javadoc pourMath.round
ou dans l'aperçu de laMath
classe.Cela semble être un bogue connu (bogue Java 6430675: Math.round a un comportement surprenant pour 0x1.fffffffffffffp-2 ) qui a été corrigé dans Java 7.
la source
Code source dans JDK 6:
Code source dans JDK 7:
Lorsque la valeur est 0.4999999999999999994d, dans JDK 6, il appellera floor et renvoie donc 1, mais dans JDK 7, la
if
condition vérifie si le nombre est la plus grande valeur double inférieure à 0,5 ou non. Comme dans ce cas, le nombre n'est pas la plus grande valeur double inférieure à 0,5, leelse
bloc renvoie donc 0.Vous pouvez essayer 0.4999999999999999999d, qui renverra 1, mais pas 0, car il s'agit de la plus grande valeur double inférieure à 0,5.
la source
floor
méthode l'arrondit correctement.J'ai le même sur JDK 1.6 32 bits, mais sur Java 7 64 bits, j'ai 0 pour 0.4999999999999999994 qui est arrondi à 0 et la dernière ligne n'est pas imprimée. Il semble que ce soit un problème de machine virtuelle, cependant, en utilisant des virgules flottantes, vous devez vous attendre à ce que les résultats diffèrent un peu selon les environnements (CPU, mode 32 ou 64 bits).
Et, lorsque vous utilisez
round
ou inversez des matrices, etc., ces bits peuvent faire une énorme différence.Sortie x64:
la source
La réponse ci- dessous est un extrait d'un rapport de bogue Oracle 6430675 sur. Consultez le rapport pour l'explication complète.
Les méthodes {Math, StrictMath.round sont définies opérationnellement comme
pour les arguments doubles. Bien que cette définition fonctionne généralement comme prévu, elle donne le résultat surprenant de 1, plutôt que de 0, pour 0x1.fffffffffffffp-2 (0.4999999999999999994).
La valeur 0,49999999999999994 est la plus grande valeur en virgule flottante inférieure à 0,5. En tant que littéral hexadécimal à virgule flottante, sa valeur est 0x1.fffffffffffffp-2, qui est égale à (2 - 2 ^ 52) * 2 ^ -2. == (0,5 - 2 ^ 54). Par conséquent, la valeur exacte de la somme
est 1 - 2 ^ 54. Ceci est à mi-chemin entre les deux nombres à virgule flottante adjacents (1 - 2 ^ 53) et 1. Dans le arrondi arithmétique IEEE 754 au mode d'arrondi pair le plus proche utilisé par Java, lorsqu'un résultat à virgule flottante est inexact, le plus proche des deux des valeurs représentatives en virgule flottante qui encadrent le résultat exact doivent être renvoyées; si les deux valeurs sont également proches, celle dont son dernier bit zéro est renvoyé. Dans ce cas, la valeur de retour correcte de l'addition est 1, pas la plus grande valeur inférieure à 1.
Alors que la méthode fonctionne comme définie, le comportement sur cette entrée est très surprenant; la spécification pourrait être modifiée en quelque chose de plus comme «Arrondir à la longueur la plus proche, arrondir les liens», ce qui permettrait de modifier le comportement de cette entrée.
la source