2010-02-09 10 views
12

Supongamos que hago esta operación:Con IEEE-754, 0 <ABS (const) <1, ¿está garantizado (x/const) * devolver resultados distintos para valores distintos de X?

(X/const) * const 

con argumentos de doble precisión como se define por IEEE 754-2008, la división primero, y luego la multiplicación.

const está en la gama 0 < ABS(const) < 1.

Suponiendo que la operación tiene éxito (no se producen desbordamientos), ¿hay argumentos distintos de X en esta operación que garanticen resultados distintos?

En otras palabras, ¿hay alguna X1, X2 y 0 < ABS(const) < 1 modo que X1 <> X2, pero (X1/const) * const = (X2/const) * const?

+1

Por "distintos argumentos de X" ¿quiere decir distinto en las variables dobles o distinto en la asignación a esas variables dobles? Por ejemplo, "double X1 = 9007199254740992ULL;" y "double X2 = 9007199254740993ULL;" son asignaciones distintas pero tienen el mismo valor en la variable (9007199254740992). –

+0

Él significa valores distintos (la definición matemática) asignaciones no distintas. – Broam

+1

Para mí, la "definición matemática" es exactamente el ejemplo que mostré. Matemáticamente, son distintos; redondeado en un doble, no lo son. –

Respuesta

7

Sí.

 
public class TestDoubleDivision 
{ 
    public static void main(String[] args) 
    { 
     final Random random = new Random(); 
     int i = 0; 
     while (i < 10) 
     { 
      final double c = random.nextDouble(); 
      final double x1 = 10.0 * random.nextDouble(); 
      final double x2 = nextDouble(x1); 

      if (x1/c * c == x2/c * c) 
      { 
       System.out.printf("x1 = %.20f, x2 = %.20f, c = %.20f\n", x1, x2, c); 
       i++; 
      } 
     } 
    } 


    private static double nextDouble(double d1) 
    { 
     return Double.longBitsToDouble(Double.doubleToLongBits(d1) + 1); 
    } 
} 

impresiones

 
x1 = 5.77383813703796800000, x2 = 5.77383813703796900000, c = 0.15897456707659440000 
x1 = 2.97635611350670850000, x2 = 2.97635611350670900000, c = 0.15347615678619309000 
x1 = 7.98634439050267450000, x2 = 7.98634439050267500000, c = 0.83202322046715640000 
x1 = 0.11618686267768408000, x2 = 0.11618686267768409000, c = 0.09302449134082225000 
x1 = 0.98646731978098480000, x2 = 0.98646731978098490000, c = 0.40549842805620606000 
x1 = 3.95828649870362700000, x2 = 3.95828649870362750000, c = 0.75526917984495820000 
x1 = 1.65404856207794440000, x2 = 1.65404856207794460000, c = 0.14500102367827516000 
x1 = 5.72713430182017500000, x2 = 5.72713430182017550000, c = 0.68241935505532810000 
x1 = 3.71143195248990980000, x2 = 3.71143195248991000000, c = 0.21294683305890750000 
x1 = 5.66441726170857800000, x2 = 5.66441726170857900000, c = 0.69355199625947250000 
+0

Justo lo que quería ver, gracias. – Quassnoi

+0

Sería bueno ver los valores verdaderos de x1, x2 yc, en lugar de los valores redondeados que se imprimen. Si tuviera que ejecutar esto en Linux utilizando printf ("% 1.53f", ...), podríamos ver los valores verdaderos. –

+0

Los valores redondeados deberían ser suficientes para reconstruir el valor exacto. En realidad, esto es Java 1.6 ejecutándose en Linux, y no hay más que ver con ceros, incluso para% 1.53f. – starblue

2

(yo sólo quería añadir algo a Starblue respuesta -. Que es demasiado largo para caber en un comentario)

Me resulta más fácil ver lo que está pasando - y espero que tú también lo quieras - cuando pueda ver el valor binario completo de un doble. Puse los ejemplos de starblue en un programa C y convertí el resultado a binario (usando mi programa de conversión al http://www.exploringbinary.com/converting-floating-point-numbers-to-binary-strings-in-c/). Aquí está la salida, más el resultado del cálculo:

 
x1 = 101.1100011000011010010000011001001011111001110000111 
x2 = 101.11000110000110100100000110010010111110011100001111 
c = 0.00101000101100101000111010100110011111010101111100001 
r = 100100.01010001101110101101000101101100011111011010101 

x1 = 10.111110011111001001111001011010001100001011001111011 
x2 = 10.1111100111110010011110010110100011000010110011111 
c = 0.0010011101001010001101101010001000011100110010101011 
r = 10011.011001001001100010101001001110011100011111011111 

x1 = 111.1111110010000001000100001110001111001101010100101 
x2 = 111.11111100100000010001000011100011110011010101001011 
c = 0.11010100111111110111100101001001011010110100010111011 
r = 1001.100110010100010010100101110100000100000110000011 

x1 = 0.0001110110111110011011000001011101101100111011010101 
x2 = 0.00011101101111100110110000010111011011001110110101010001 
c = 0.0001011111010000011100111111110000001001001011101001 
r = 1.00111111101111011111001110101010100101010101010101 

x1 = 0.1111110010001001000111110100110100001000001101111111 
x2 = 0.11111100100010010001111101001101000010000011011111111 
c = 0.01100111110011101011111010110111000101001011000000111 
r = 10.011011101100011101000000101000110110101011010011111 

x1 = 11.1111010101010010010000111001010000100001011000111 
x2 = 11.111101010101001001000011100101000010000101100011101 
c = 0.110000010101100101010010001010110001110001011111111 
r = 101.00111101101010110100110000011111101001010010101111 

x1 = 1.1010011101101111101110100000000000011110110111110111 
x2 = 1.1010011101101111101110100000000000011110110111111 
c = 0.00100101000111101100100101111110100101011010111111001 
r = 1011.011010000011101100001011000110000010011111110001 

x1 = 101.10111010001001010111100100111110000111100001000011 
x2 = 101.101110100010010101111001001111100001111000010001 
c = 0.101011101011001100001000111011000001111010111011011 
r = 1000.0110010001110100001001010000000101111000011111011 

x1 = 11.101101100010000001100111100010010100011000001001111 
x2 = 11.10110110001000000110011110001001010001100000101 
c = 0.0011011010000011101011110000001111000110010101111111 
r = 10001.01101101110011010100011111101110101011001010001 

x1 = 101.10101010000101110011111111101001111011111010101111 
x2 = 101.1010101000010111001111111110100111101111101011 
c = 0.1011000110001100100111111010011000000010100011 
r = 1000.0010101011010001010101111000111101110100001000001 

(Por cierto, la parte de "* const" de la expresión es innecesaria:. La división por const solo muestra que X1/const == X2/const)

Puede ver realmente lo que sucede cuando compara los valores dobles con los valores de precisión verdaderos y arbitrarios. Tome el primer ejemplo, por ejemplo:

 
x1/c = x2/c (double) = 100100.01010001101110101101000101101100011111011010101 

x1/c (true)   = 100100.01010001101110101101000101101100011111011010100 1011... 

x2/c (true)   = 100100.01010001101110101101000101101100011111011010101 0111... 

I poner un espacio entre bits significativos 53 y 54, en el que el redondeo se produce en un doble. x1/c redondea hacia arriba, y x2/c redondea hacia abajo (trunca), convirtiéndose en el mismo valor.

+0

@Rick: con respecto a "expresión no necesaria": ya que 'const' es menor que' 1', puede suceder de modo que los cocientes sean diferentes pero los productos no lo son. Cuando pregunté esto, no estaba seguro de que los cocientes pudieran diferir (y me sorprendió, pero el hecho es que pueden). – Quassnoi

+0

Estaba señalando que para estos diez ejemplos, solo la división causó el "problema" (los probé todos en mi máquina). –

Cuestiones relacionadas