Il tuo istinto è fondamentalmente giusto, l'ordinamento in ordine crescente (di grandezza) di solito migliora un po 'le cose. Considera il caso in cui stiamo aggiungendo float a precisione singola (32 bit) e ci sono 1 miliardo di valori uguali a 1 / (1 miliardo) e un valore uguale a 1. Se 1 viene prima, la somma verrà a 1, poiché 1 + (1/1 miliardo) è 1 a causa della perdita di precisione. Ogni aggiunta non ha alcun effetto sul totale.
Se i valori piccoli vengono prima, si sommeranno almeno a qualcosa, anche se anche in questo caso ne ho 2 ^ 30, mentre dopo 2 ^ 25 circa sono tornato nella situazione in cui ciascuno individualmente non influisce sul totale più. Quindi avrò ancora bisogno di altri trucchi.
Questo è un caso estremo, ma in generale l'aggiunta di due valori di grandezza simile è più accurata dell'aggiunta di due valori di grandezze molto diverse, dal momento che in questo modo "scarti" meno bit di precisione nel valore più piccolo. Ordinando i numeri, si raggruppano insieme valori di grandezza simile, e aggiungendoli in ordine crescente si dà ai valori piccoli una "possibilità" di raggiungere cumulativamente la grandezza dei numeri più grandi.
Tuttavia, se sono coinvolti numeri negativi, è facile "superare in astuzia" questo approccio. Consideriamo tre valori per riassumere, {1, -1, 1 billionth}
. La somma aritmeticamente corretta è 1 billionth
, ma se la mia prima aggiunta coinvolge il valore minuscolo, la mia somma finale sarà 0. Dei 6 possibili ordini, solo 2 sono "corretti" - {1, -1, 1 billionth}
e {-1, 1, 1 billionth}
. Tutti e 6 gli ordini danno risultati accurati alla scala del valore di grandezza massima nell'input (0,0000001% in uscita), ma per 4 di essi il risultato è impreciso alla scala della vera soluzione (100% in uscita). Il problema particolare che stai risolvendo ti dirà se il primo è abbastanza buono o meno.
In effetti, puoi giocare molti più trucchi che aggiungerli semplicemente in ordine ordinato. Se hai molti valori molto piccoli, un numero medio di valori medi e un numero piccolo di valori grandi, allora potrebbe essere più accurato prima sommare tutti i valori piccoli, quindi sommare separatamente quelli medi, aggiungere quei due totali insieme poi aggiungi quelli grandi. Non è affatto banale trovare la combinazione più accurata di aggiunte in virgola mobile, ma per far fronte a casi veramente brutti puoi mantenere un'intera serie di totali correnti a diverse grandezze, aggiungere ogni nuovo valore al totale che meglio corrisponde alla sua grandezza, e quando un totale parziale inizia a diventare troppo grande per la sua grandezza, aggiungilo al totale successivo e avviane uno nuovo. Portato al suo estremo logico, questo processo equivale a eseguire la somma in un tipo di precisione arbitraria (quindi si ' farlo). Ma data la scelta semplicistica di aggiungere in ordine di grandezza crescente o decrescente, l'ascesa è la soluzione migliore.
Ha qualche relazione con la programmazione del mondo reale, poiché ci sono alcuni casi in cui il tuo calcolo può andare molto male se tagli accidentalmente una coda "pesante" composta da un gran numero di valori ognuno dei quali è troppo piccolo per essere influenzato individualmente la somma, o se si butta via troppa precisione da molti piccoli valori che singolarmente influenzano solo gli ultimi bit della somma. Nei casi in cui la coda è comunque trascurabile, probabilmente non ti interessa. Ad esempio, se stai sommando solo un piccolo numero di valori in primo luogo e stai usando solo poche cifre significative della somma.