Ecco cosa sta succedendo in binario. Come sappiamo, alcuni valori in virgola mobile non possono essere rappresentati esattamente in binario, anche se possono essere rappresentati esattamente in decimali. Questi 3 numeri sono solo esempi di questo fatto.
Con questo programma ho prodotto le rappresentazioni esadecimali di ciascun numero e i risultati di ogni aggiunta.
public class Main{
public static void main(String args[]) {
double x = 23.53; // Inexact representation
double y = 5.88; // Inexact representation
double z = 17.64; // Inexact representation
double s = 47.05; // What math tells us the sum should be; still inexact
printValueAndInHex(x);
printValueAndInHex(y);
printValueAndInHex(z);
printValueAndInHex(s);
System.out.println("--------");
double t1 = x + y;
printValueAndInHex(t1);
t1 = t1 + z;
printValueAndInHex(t1);
System.out.println("--------");
double t2 = x + z;
printValueAndInHex(t2);
t2 = t2 + y;
printValueAndInHex(t2);
}
private static void printValueAndInHex(double d)
{
System.out.println(Long.toHexString(Double.doubleToLongBits(d)) + ": " + d);
}
}
Il printValueAndInHexmetodo è solo un aiuto per la stampante esadecimale.
L'output è il seguente:
403787ae147ae148: 23.53
4017851eb851eb85: 5.88
4031a3d70a3d70a4: 17.64
4047866666666666: 47.05
--------
403d68f5c28f5c29: 29.41
4047866666666666: 47.05
--------
404495c28f5c28f6: 41.17
4047866666666667: 47.050000000000004
I primi 4 numeri sono x, y, z, e s's rappresentazioni esadecimali. Nella rappresentazione in virgola mobile IEEE, i bit 2-12 rappresentano l' esponente binario , ovvero la scala del numero. (Il primo bit è il bit di segno e i bit rimanenti per la mantissa .) L'esponente rappresentato è in realtà il numero binario meno 1023.
Gli esponenti per i primi 4 numeri vengono estratti:
sign|exponent
403 => 0|100 0000 0011| => 1027 - 1023 = 4
401 => 0|100 0000 0001| => 1025 - 1023 = 2
403 => 0|100 0000 0011| => 1027 - 1023 = 4
404 => 0|100 0000 0100| => 1028 - 1023 = 5
Prima serie di aggiunte
Il secondo numero ( y) è di grandezza minore. Quando si aggiungono questi due numeri per ottenere x + y, gli ultimi 2 bit del secondo numero ( 01) vengono spostati fuori dall'intervallo e non figurano nel calcolo.
La seconda aggiunta aggiunge x + ye zaggiunge due numeri della stessa scala.
Seconda serie di aggiunte
Qui, si x + zverifica per primo. Sono della stessa scala, ma producono un numero che è più in alto nella scala:
404 => 0|100 0000 0100| => 1028 - 1023 = 5
La seconda aggiunta aggiunge x + ze y, e ora vengono rilasciati 3 bit yper aggiungere i numeri ( 101). Qui, ci deve essere un arrotondamento verso l'alto, perché il risultato è il prossimo numero in virgola mobile verso l'alto: 4047866666666666per la prima serie di aggiunte rispetto 4047866666666667alla seconda serie di aggiunte. Tale errore è abbastanza significativo da mostrare nella stampa del totale.
In conclusione, fai attenzione quando esegui operazioni matematiche sui numeri IEEE. Alcune rappresentazioni sono inesatte e diventano ancora più inesatte quando le scale sono diverse. Aggiungi e sottrai numeri di scala simile se puoi.
(2.0^53 + 1) - 1 == 2.0^53 - 1 != 2^53 == 2^53 + (1 - 1).). Quindi sì: fai attenzione quando scegli l'ordine delle somme e altre operazioni. Alcuni linguaggi offrono un built-in per eseguire somme "di alta precisione" (ad es. Di Pythonmath.fsum), quindi potresti prendere in considerazione l'uso di queste funzioni invece dell'algoritmo della somma ingenua.