2010-10-10 11 views
7

¿Hay algo roto o no entiendo lo que está sucediendo?Cómo alterar el doble en su incremento más pequeño

static String getRealBinary(double val) { 
    long tmp = Double.doubleToLongBits(val); 
    StringBuilder sb = new StringBuilder(); 

    for (long n = 64; --n > 0; tmp >>= 1) 
     if ((tmp & 1) == 0) 
      sb.insert(0, ('0')); 
     else 
      sb.insert(0, ('1')); 

    sb.insert(0, '[').insert(2, "] [").insert(16, "] [").append(']'); 
    return sb.toString(); 
} 

public static void main(String[] argv) { 
    for (int j = 3; --j >= 0;) { 
     double d = j; 
     for (int i = 3; --i >= 0;) { 
      d += Double.MIN_VALUE; 
      System.out.println(d +getRealBinary(d)); 
     } 
    } 
} 

Con salida:

2.0[1] [00000000000] [000000000000000000000000000000000000000000000000000] 
2.0[1] [00000000000] [000000000000000000000000000000000000000000000000000] 
2.0[1] [00000000000] [000000000000000000000000000000000000000000000000000] 
1.0[0] [11111111110] [000000000000000000000000000000000000000000000000000] 
1.0[0] [11111111110] [000000000000000000000000000000000000000000000000000] 
1.0[0] [11111111110] [000000000000000000000000000000000000000000000000000] 
4.9E-324[0] [00000000000] [000000000000000000000000000000000000000000000000001] 
1.0E-323[0] [00000000000] [000000000000000000000000000000000000000000000000010] 
1.5E-323[0] [00000000000] [000000000000000000000000000000000000000000000000011] 
+4

¿Qué estás tratando de hacer? ¿Cuál es tu pregunta? – Sjoerd

+0

Mi pregunta es: "Cómo alterar el doble en su incremento más pequeño", y este es mi esfuerzo que falló. – Margus

+1

porque no solo editas esos bits si quieres hacer el incremento más pequeño, falla para 1 y 2 porque MIN_VALUE es muy pequeño (realmente pequeño) así que 0 + realmente pequeño = muy pequeño, pero 2+ realmente pequeño ~ = 2 es debido a Punto flotante: min_value está con el punto lo más a la izquierda posible, mientras que para dos está en algún lugar en el medio, left pierde. puede ver sus aproximadamente 300 dígitos después del punto que la diferencia debería ser, mientras que una doble almacena solo alrededor de 15-20 dígitos significativos. – flownt

Respuesta

8

La idea general es primero convertir el doble de su larga representación (usando doubleToLongBits como lo han hecho en getRealBinary), incremento que de largo por 1, y finalmente convertir el nuevo tiempo de vuelta al doble que representa a través de longBitsToDouble.

EDITAR: Java (desde 1.5) proporciona Math.ulp(double), que supongo que puede utilizar para calcular el siguiente valor más alto directamente así: x + Math.ulp(x).

+2

Desde Java 1.6, hay ['Math.nextAfter (start, direction))] (http://docs.oracle.com/javase/7/docs/api/java/lang/Math.html#nextAfter (double, % 20double)), que es mucho más confiable. Incluso maneja casos especiales como alrededor de cero. – z0r

7

números de punto flotante no se extienden uniformemente sobre la línea de números como tipos de enteros son. Están más densamente empaquetados cerca de 0 y muy separados a medida que te acercas al infinito. Por lo tanto, no hay una constante que pueda agregar a un número de coma flotante para llegar al siguiente número de coma flotante.

+0

¿Cómo puede ser esto? Tenemos una mantisa y un exponente. Y ninguno de los dos favorece 0. –

+0

@TonyEnnis: El exponente es solo eso: un exponente. Oversimplified un poco, el valor de un número de punto flotante es algo así como 'mantissa * 2^exponente'. Es decir, la cantidad por la cual cambiar la mantisa cambia el número resultante depende completamente del valor del exponente. Cuanto menor sea el exponente, menor será el cambio. – cHao

4

Su código no está bien formado. Intenta agregar el valor doble mínimo y espera que el resultado sea diferente del valor original. El problema es que double.MinValue es tan pequeño que el resultado se redondea y no se ve afectado.

Lectura recomendada: http://en.wikipedia.org/wiki/Machine_epsilon

En el artículo de Wikipedia no es el código de Java también. Epsilon es por definición el número más pequeño, como (X + eps * X! = X), y eps * X se llama "relativo-épsilon"

0

En caso de que desee utilizar la clase BigDecimal, existe el método BigDecimal.ulp() también.

1

Desde Java 1.8 hay java.lang.Math.nextUp(double) haciendo exactamente lo que quiere. También hay frente a java.lang.Math.nextDown(double).

Cuestiones relacionadas