Twój instynkt ma w zasadzie rację, sortowanie w rosnącym porządku (wielkości) zwykle nieco poprawia sytuację. Rozważmy przypadek, w którym dodajemy zmiennoprzecinkowe pojedynczej precyzji (32-bitowe) i mamy 1 miliard wartości równych 1 / (1 miliard) i jedną wartość równą 1. Jeśli 1 pojawi się jako pierwsza, to suma przyjdzie do 1, ponieważ 1 + (1/1 miliarda) to 1 z powodu utraty precyzji. Każdy dodatek nie ma żadnego wpływu na całość.
Jeśli małe wartości pojawią się pierwsze, to przynajmniej zsumują się do czegoś, chociaż nawet wtedy mam ich 2 ^ 30, a po około 2 ^ 25 wracam do sytuacji, w której każda z osobna nie wpływa na sumę już więcej. Więc nadal będę potrzebować więcej sztuczek.
To skrajny przypadek, ale generalnie dodanie dwóch wartości o podobnej wielkości jest dokładniejsze niż dodanie dwóch wartości o bardzo różnych wielkościach, ponieważ w ten sposób „odrzuca się” mniej bitów precyzji z mniejszej wartości. Sortując liczby, grupujesz razem wartości o podobnej wielkości, a dodając je w porządku rosnącym, dajesz małym wartościom „szansę” na skumulowane osiągnięcie wielkości większych liczb.
Jednak jeśli chodzi o liczby ujemne, łatwo jest „przechytrzyć” to podejście. Rozważmy trzy wartości w sumie {1, -1, 1 billionth}
. Suma poprawna arytmetycznie to 1 billionth
, ale jeśli moje pierwsze dodanie obejmuje małą wartość, wtedy moja suma końcowa wyniesie 0. Z 6 możliwych zamówień tylko 2 są „poprawne” - {1, -1, 1 billionth}
i {-1, 1, 1 billionth}
. Wszystkie 6 rzędów dają wyniki, które są dokładne w skali największej wartości na wejściu (0,0000001% na zewnątrz), ale dla 4 z nich wynik jest niedokładny w skali rzeczywistego rozwiązania (100% poza). Konkretny problem, który rozwiązujesz, powie ci, czy ten pierwszy jest wystarczająco dobry, czy nie.
W rzeczywistości możesz grać o wiele więcej sztuczek, niż tylko dodawać je w posortowanej kolejności. Jeśli masz wiele bardzo małych wartości, średnią liczbę średnich wartości i niewielką liczbę dużych wartości, najdokładniejsze może być najpierw zsumowanie wszystkich małych, a następnie osobne zsumowanie średnich i dodanie tych dwóch sum razem, a następnie dodaj duże. Znalezienie najdokładniejszej kombinacji dodawań zmiennoprzecinkowych nie jest wcale trywialne, ale aby poradzić sobie z naprawdę złymi przypadkami, możesz zachować cały szereg bieżących sum o różnych wielkościach, dodać każdą nową wartość do sumy, która najlepiej pasuje do jej wielkości, a kiedy suma bieżąca zacznie być zbyt duża dla swojej wielkości, dodaj ją do następnej sumy i rozpocznij nową. Doprowadzony do jego logicznego ekstremum, proces ten jest równoważny wykonaniu sumy w typie z dowolną precyzją (więc zrób to). Biorąc jednak pod uwagę uproszczony wybór dodawania rosnącego lub malejącego rzędu wielkości, zwiększanie jest lepszym rozwiązaniem.
Ma to pewien związek z programowaniem w świecie rzeczywistym, ponieważ istnieją przypadki, w których obliczenia mogą pójść bardzo źle, jeśli przypadkowo odetniesz „ciężki” ogon składający się z dużej liczby wartości, z których każda jest zbyt mała, aby mieć na nią indywidualny wpływ suma lub jeśli odrzucisz zbyt dużą precyzję z wielu małych wartości, które indywidualnie wpływają tylko na kilka ostatnich bitów sumy. W przypadkach, gdy ogon i tak jest znikomy, prawdopodobnie nie obchodzi cię to. Na przykład, jeśli na początku dodajesz tylko niewielką liczbę wartości i używasz tylko kilku znaczących cyfr z sumy.