2012-11-20 22 views
7

È possibile stabilire, anche approssimativamente, quale sarebbe la massima perdita di precisione quando si gestiscono due valori double in java (aggiunta/sottrazione)? Probabilmente lo scenario peggiore è quando due numeri non possono essere rappresentati esattamente, e quindi viene eseguita un'operazione su di essi, che si traduce in un valore che non può essere rappresentato anche esattamente.Java: massima perdita di precisione in una doppia aggiunta/sottrazione

+0

Se la perdita di precisione è un problema, ti consiglio di utilizzare BigDecimal anziché double. –

+0

Sì, il punto centrale di questa domanda è vedere quanto è grande la perdita. il doppio occupa 8 byte, mentre BigDecimal attorno ai 40 e ho bisogno di memorizzare molti punti di dati. – Bober02

+0

Quando si eseguono molte operazioni in virgola mobile, il guadagno in termini di prestazioni dell'uso delle primitive anziché degli oggetti può essere significativo. – Cephalopod

risposta

5

Dai un'occhiata allo Math.ulp(double). Il ulp di un double è il delta al successivo valore più alto. Ad esempio, se si aggiungono numeri e uno è inferiore all'ulp dell'altro, si sa che l'aggiunta non avrà alcun effetto. Se si moltiplicano due doppi, è possibile moltiplicare le loro ulp per ottenere l'errore massimo del risultato.

+0

Questo è esattamente quello che volevo – Bober02

7

Il caso peggiore è che tutta la precisione può essere persa. Questo può accadere ad esempio se il risultato è maggiore del numero finito rappresentabile più grande. Quindi verrà memorizzato come POSITIVE_INFINITY (o NEGATIVE_INFINITY).

Per quanto riguarda l'aggiornamento, può succedere con l'aggiunta.

double a = Double.MAX_VALUE; 
System.out.println(a); 
double b = a + a; 
System.out.println(b); 

Risultato:

1.7976931348623157E308 
Infinity 

Vedi on-line: ideone

In generale, la dimensione dell'errore rappresentazione è relativo alla dimensione dei vostri numeri.

+0

Vedere le modifiche sopra - Intendevo principalmente aggiunta ... – Bober02

0

Si potrebbe avere uno sguardo alla effettiva precisione dei vostri input, ad esempio il codice qui sotto uscite:

input: 0.01000000000000000020816681711721685132943093776702880859375 
range: [0.0099999999999999984734433411404097569175064563751220703125 - 0.010000000000000001942890293094023945741355419158935546875] 
range size: 3.4694469519536141888238489627838134765625E-18 
input: 10000000000000000 
range: [9999999999999998 - 10000000000000002] 
range size: 4 
public static void main(String[] args) { 
    printRange(0.01); 
    printRange(10000000000000000d); 
} 

private static void printRange(double d) { 
    long dBits = Double.doubleToLongBits(d); 
    double dNext = Double.longBitsToDouble(dBits + 1); 
    double dPrevious = Double.longBitsToDouble(dBits + -1); 
    System.out.println("input: " + new BigDecimal(d)); 
    System.out.println("range: [" + new BigDecimal(dPrevious) + " - " + new BigDecimal(dNext) + "]"); 
    System.out.println("range size: " + new BigDecimal(dNext - dPrevious)); 
} 

Si avrebbe ancora bisogno di poi stimare la perdita sul risultato della vostra operazione. E questo non funziona con i casi d'angolo (attorno a Infinity, NaN ecc.).

Problemi correlati