2010-10-10 8 views
7

C'è qualcosa di rotto o non riesco a capire cosa sta succedendo?Come modificare il doppio con il suo più piccolo incremento

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 uscita:

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

Cosa stai cercando di fare? Qual è la tua domanda? – Sjoerd

+0

La mia domanda è: "Come modificare il doppio con il suo più piccolo incremento", e questo è il mio sforzo che cosa è fallito. – Margus

+1

perché non solo modificare quei bit se si vuole fare l'incremento più piccolo, fallisce per 1 e 2 perché MIN_VALUE è piccolo (molto piccolo) quindi 0 + veramente piccolo = veramente piccolo, ma 2+ molto piccolo ~ = 2 è causa di Il punto FLOATING il valore min è con il punto il più a sinistra possibile mentre per due è da qualche parte nel mezzo, a sinistra perde. puoi vedere le sue circa 300 cifre dopo il punto che la differenza dovrebbe essere, mentre un doppio memorizza solo circa 15-20 cifre significative. – flownt

risposta

8

L'idea generale è prima convertire il doppio della sua lunga rappresentazione (usando doubleToLongBits come avete fatto in getRealBinary), incremento che di lunghezza per 1, e, infine, convertire il nuovo lungo torna alla doppia che rappresenta tramite longBitsToDouble.

MODIFICA: Java (dal 1.5) fornisce Math.ulp(double), che suppongo che sia possibile utilizzare per calcolare direttamente il valore più alto direttamente così: x + Math.ulp(x).

+2

Da Java 1.6, c'è ['Math.nextAfter (start, direction)'] (http://docs.oracle.com/javase/7/docs/api/java/lang/Math.html#nextAfter (doppio, % 20double)), che è molto più affidabile. Gestisce anche casi speciali come intorno a zero. – z0r

7

numeri in virgola mobile non sono distribuite uniformemente sulla linea altrettanti tipi interi sono. Sono più densamente imballati vicino a 0 e molto distanti quando ti avvicini all'infinito. Pertanto non esiste una costante che sia possibile aggiungere a un numero in virgola mobile per raggiungere il successivo numero in virgola mobile.

+0

Come può essere? Abbiamo una mantissa e un esponente. E neanche il favore 0. –

+0

@TonyEnnis: L'esponente è proprio questo - un esponente. Oversimplified un po ', il valore di un numero in virgola mobile è qualcosa come 'mantissa * 2^esponente'. Cioè, la quantità con cui cambiare la mantissa cambia il numero risultante dipende interamente dal valore dell'esponente. Più basso è l'esponente, minore sarà il cambiamento. – cHao

4

Il tuo codice non è ben formato. Si tenta di aggiungere il doppio valore minimo e si aspetta che il risultato sia diverso dal valore originale. Il problema è che double.MinValue è così piccolo che il risultato è arrotondato e non viene influenzato.

Lettura consigliata: http://en.wikipedia.org/wiki/Machine_epsilon

Sul articolo di Wikipedia c'è il codice Java troppo. Epsilon è per definizione il numero più piccolo come (X + eps * X! = X) ed eps * X è chiamato "relativo-epsilon"

0

Nel caso in cui si desideri utilizzare la classe BigDecimal, esiste il metodo BigDecimal.ulp() anche.

1

Da Java 1.8 c'è java.lang.Math.nextUp(double) facendo esattamente ciò che si desidera. C'è anche l'opposto java.lang.Math.nextDown(double).

Problemi correlati