2010-12-03 27 views
12

Estoy volviendo a consultar una pregunta (How to test if numeric conversion will change value?) que, en lo que a mí respecta, estaba completamente resuelta. El problema era detectar cuándo un valor numérico particular sobrecargaría el tipo de número IEEE-754 de JavaScript. La pregunta anterior era usar C# y la respuesta marcada funcionó perfectamente.IEEE-754 Doble (coma flotante de 64 bits) vs. Largo (Entero de 64 bits) Revisited

Ahora estoy haciendo exactamente la misma tarea pero esta vez en Java y no funciona. AFAIK, Java usa IEEE-754 para su tipo de datos doble. Así que debería ser capaz de lanzarlo hacia adelante y hacia atrás para forzar la pérdida de precisión, pero los viajes redondos. Desconcertado por esto comencé a profundizar en Java y ahora estoy realmente confundido.

Tanto en C# y Java, los valores mínimo y máximo para una larga son los mismos:

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

AFAIK, estos valores están fuera de los números representables en IEEE-754 debido a los bits fijos reservados para el exponente y firme.

// 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!"); } 

Esto devuelve false para (valor = -9223372036854775808L) en Java:

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

Esto devuelve false para (valor = -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 roundtrip = Double.valueOf(Double.longBitsToDouble(bits)).longValue(); 
    return (value != roundtrip); 
} 

Esto devuelve true para (valor = -9223372036854775808L) pero es menos preciso:

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

¿Por qué funciona de esta manera? ¿Me falta algo como la optimización del compilador, p. ¿El compilador detecta mis conversiones y las "arregla" por mí?

Edición: Agregar caso de prueba por solicitud. Los tres de estas pruebas falla:

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 roundtrip = Double.valueOf(Double.longBitsToDouble(bits)).longValue(); 

     assertTrue(Long.MIN_VALUE != roundtrip); 
    } 

    @Test 
    public void ieee754Three() { 
     long bits = Double.doubleToRawLongBits(Long.valueOf(Long.MIN_VALUE).doubleValue()); 
     long roundtrip = Double.valueOf(Double.longBitsToDouble(bits)).longValue(); 

     assertTrue(Long.MIN_VALUE != roundtrip); 
    } 
} 
+0

Ambos métodos devuelven 'false' para mí. JDK 1.6.0_21. – axtavt

+0

¡Interesante! Ahora estoy realmente confundido. El mío se está ejecutando a través de JUnit, pero también debe estar en JDK 1.6.0_21. – mckamey

+0

¿Puedes mostrar el caso de prueba completo ?. – axtavt

Respuesta

6

-9223372036854775808Les representable como un número de doble precisión IEEE-754. Es exactamente -2^63, que tiene la doble representación -1.0 x 2^63 y la codificación 0xc3e0000000000000.

Doble es capaz de representar números mucho, mucho más grandes que esto. Sin embargo, no es capaz de representar todos los enteros en el rango de números representables. Por ejemplo, si agrega uno al número, obtendrá -9223372036854775807 = -2^63 + 1, que es no representable como un valor de precisión doble, y no sobrevivirá a la conversión de ida y vuelta.

Convertir -2^63 + 1 en double lo redondeará al valor doble representable más cercano, que es -2^63; la conversión de nuevo a largo conservará ese valor.

Editar: ¿En qué plataforma hiciste pruebas de JavaScript? En Safari actual,

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

evalúa como True.

+0

También es interesante. ¡Parece que elijo un mal caso de prueba! ¿Por qué funcionaría en C# sin embargo? Eso me confunde. – mckamey

+0

@McKAMEY: Si realmente "funciona" en C#, eso es contrario al estándar IEEE-754. ¿Has observado esto? –

+0

Sí, fue mi caso de prueba y "pasó" :) Luego lo moví a Java y no fue así. – mckamey

Cuestiones relacionadas