Wie fasst man am besten viele Gleitkommazahlen zusammen?

Stellen Sie sich vor, Sie haben eine große Anzahl von Gleitkommazahlen aller Größen. Was ist der korrekteste Weg, um die Summe mit dem geringsten Fehler zu berechnen? Wenn das Array beispielsweise wie folgt aussieht:

[1.0, 1e-10, 1e-10, ... 1e-10.0]

und Sie addieren von links nach rechts mit einer einfachen Schleife, wie

sum = 0
numbers.each do |val|
    sum += val
end

Wenn Sie die kleineren Zahlen addieren, wird der Genauigkeitsschwellenwert möglicherweise unterschritten, sodass der Fehler immer größer wird. Soweit ich weiß, besteht der beste Weg darin, das Array zu sortieren und die Zahlen von der niedrigsten zur höchsten zu addieren, aber ich frage mich, ob es einen noch besseren Weg gibt (schneller, präziser

BEARBEITE: Danke für die Antwort, ich habe jetzt einen funktionierenden Code, der doppelte Werte in Java perfekt zusammenfasst. Es ist ein direkter Port aus dem Python-Post der Gewinnerantwort. Die Lösung besteht alle meine Unit-Tests. (Eine längere, aber optimierte Version davon finden Sie hier Summarizer.java)

/**
 * Adds up numbers in an array with perfect precision, and in O(n).
 * 
 * @see http://code.activestate.com/recipes/393090/
 */
public class Summarizer {

    /**
     * Perfectly sums up numbers, without rounding errors (if at all possible).
     * 
     * @param values
     *            The values to sum up.
     * @return The sum.
     */
    public static double msum(double... values) {
        List<Double> partials = new ArrayList<Double>();
        for (double x : values) {
            int i = 0;
            for (double y : partials) {
                if (Math.abs(x) < Math.abs(y)) {
                    double tmp = x;
                    x = y;
                    y = tmp;
                }
                double hi = x + y;
                double lo = y - (hi - x);
                if (lo != 0.0) {
                    partials.set(i, lo);
                    ++i;
                }
                x = hi;
            }
            if (i < partials.size()) {
                partials.set(i, x);
                partials.subList(i + 1, partials.size()).clear();
            } else {
                partials.add(x);
            }
        }
        return sum(partials);
    }

    /**
     * Sums up the rest of the partial numbers which cannot be summed up without
     * loss of precision.
     */
    public static double sum(Collection<Double> values) {
        double s = 0.0;
        for (Double d : values) {
            s += d;
        }
        return s;
    }
}

Antworten auf die Frage(10)

Ihre Antwort auf die Frage