IEEE-754 double (virgule flottante 64 bits) vs long (entier 64 bits) revisité

Je reviens sur une question ( comment vérifier si la conversion numérique va changer de valeur? ) Qui, en ce qui me concerne, était totalement résolue. Le problème consistait à détecter le moment où une valeur numérique particulière déborderait du type de numéro IEEE-754 de JavaScript. La question précédente utilisait C # et la réponse marquée a parfaitement fonctionné.

Maintenant, je fais exactement la même tâche mais cette fois en Java et cela ne fonctionne pas. Autant que je sache, Java utilise IEEE-754 pour son type de données double. Je devrais donc pouvoir le lancer pour forcer la perte de précision, mais cela va-et-vient. Dérouté par cela, j’ai commencé à fouiller plus profondément dans Java et maintenant je suis vraiment confus.

En C # et en Java, les valeurs min et max pour long sont les mêmes:

long MIN_VALUE = -9223372036854775808L; long MAX_VALUE = 9223372036854775807L; 

Autant que je sache, ces valeurs sont en dehors des nombres représentables dans IEEE-754 en raison des bits fixes réservés à l’exposant et au signe.

 // this fails in browsers that have stuck with the pure ECMAScript Number format var str = Number(-9223372036854775808).toFixed(); if ("-9223372036854775808" !== str) { throw new Error("Overflow!"); } 

Cela renvoie false pour (valeur = -9223372036854775808L) en Java:

 boolean invalidIEEE754(long value) { try { return ((long)((double)value)) != value; } catch (Exception ex) { return true; } } 

Cela renvoie false pour (valeur = -9223372036854775808L) en Java:

 boolean invalidIEEE754(long value) { // trying to get closer to the actual representation and // being more explicit about conversions long bits = Double.doubleToLongBits(Long.valueOf(value).doubleValue()); long roundsortingp = Double.valueOf(Double.longBitsToDouble(bits)).longValue(); return (value != roundsortingp); } 

Cela retourne true pour (valeur = -9223372036854775808L) mais est moins précis:

 boolean invalidIEEE754(long value) { return (0x0L != (0xFFF0000000000000L & (value < 0L ? -value : value))); } 

Pourquoi ça marche comme ça? Est-ce qu’il me manque quelque chose comme l’optimisation du compilateur, par exemple le compilateur détecte-t-il mes conversions et les “corrige-t-il” pour moi?

Edit: Ajout de cas de test par demande. Ces trois tests échouent:

 import static org.junit.Assert.*; import org.junit.Test; public class FooTests { @Test public void ieee754One() { assertTrue(((long)((double)Long.MIN_VALUE)) != Long.MIN_VALUE); } @Test public void ieee754Two() { long bits = Double.doubleToLongBits(Long.valueOf(Long.MIN_VALUE).doubleValue()); long roundsortingp = Double.valueOf(Double.longBitsToDouble(bits)).longValue(); assertTrue(Long.MIN_VALUE != roundsortingp); } @Test public void ieee754Three() { long bits = Double.doubleToRawLongBits(Long.valueOf(Long.MIN_VALUE).doubleValue()); long roundsortingp = Double.valueOf(Double.longBitsToDouble(bits)).longValue(); assertTrue(Long.MIN_VALUE != roundsortingp); } } 

-9223372036854775808L est représentable par un nombre à double précision IEEE-754. C’est exactement -2^63 , qui a la double représentation -1.0 x 2^63 et codant 0xc3e0000000000000 .

Double est capable de représenter des nombres beaucoup plus grands que cela. Cependant, il n’est pas capable de représenter tous les entiers dans la plage des nombres représentables. Par exemple, si vous ajoutez un au nombre, vous obtiendrez -9223372036854775807 = -2^63 + 1 , ce qui n’est pas représentable en tant que valeur à double précision et ne survivra pas à la conversion aller-retour.

Convertir -2^63 + 1 en double l’arrondira à la valeur double représentant la plus proche, qui est -2^63 ; la reconversion à long préservera cette valeur.

Edit: Sur quelle plate-forme avez-vous testé JavaScript? Dans Safari actuel,

 "-9223372036854775808" === Number(-9223372036854775808).toFixed() 

évalue comme True .