如何最好地总结大量的浮点数?

时间:2008-12-26 19:40:09

标签: algorithm language-agnostic floating-point precision

想象一下,你有各种各样大小的浮点数。计算总和的最正确方法是什么,误差最小?例如,当数组看起来像这样:

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

你用一个简单的循环从左到右加起来,比如

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

每当你加起来较小的数字可能会低于精度阈值,所以错误变得越来越大。据我所知,最好的方法是对数组进行排序并开始将数字从最低到最高相加,但我想知道是否有更好的方法(更快,更精确)?

编辑:感谢您的回答,我现在有了一个可以完美地总结Java中双值的工作代码。它是获胜答案的Python帖子的直接端口。该解决方案通过了我的所有单元测试。 (这里有一个更长但优化的版本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;
    }
}

5 个答案:

答案 0 :(得分:24)

对于“更精确”:this recipe in the Python Cookbook具有保持完整精度的总和算法(通过跟踪小计)。代码是用Python编写的,但即使你不了解Python,它也足以适应任何其他语言。

所有细节都在this paper中给出。

答案 1 :(得分:13)

另见:Kahan summation algorithm它不需要O(n)存储,只需要O(1)。

答案 2 :(得分:3)

根据您的需要,有许多算法。通常他们需要跟踪部分总和。如果只保留x [k + 1] - x [k]之和,则得到Kahan算法。如果你跟踪所有的部分和(因此产生O(n ^ 2)算法),你得到@dF的答案。

请注意,除了您的问题之外,总计不同符号的数量是非常有问题的。

现在,有比简单跟踪所有部分和更简单的配方:

  • 在求和之前对数字进行排序,将所有的负数和正数相加。如果你已经排序了数字,那么你就有了O(n log n)算法。总和增加幅度。
  • 成对,然后成对等等。

个人经验表明,除了Kahan的方法,你通常不需要更高级的东西。

答案 3 :(得分:0)

好吧,如果您不想排序,那么您可以简单地将总数保存在一个变量中,其变量的精度高于单个值(例如,使用double来保持浮点数的总和,或者说“quad”保持双打的总和)。这会对性能造成损失,但可能会低于排序成本。

答案 4 :(得分:0)

如果您的应用程序依赖于数字处理搜索任意精度算术库,但我不知道是否存在此类Python库。当然,所有这些都取决于你想要多少精度数字 - 如果你小心使用它,你可以用标准IEEE浮点数获得良好的结果。