【发布时间】:2009-08-10 07:58:17
【问题描述】:
简而言之:我如何执行a+b 以使由于截断而导致的任何精度损失远离零而不是接近零?
长篇大论
我正在计算一长串浮点值的总和,以计算集合的样本均值和方差。由于 Var(X) = E(X2) - E(X)2,保持所有数字的连续计数就足够了,到目前为止所有数字的总和,以及迄今为止所有数字的平方和。
到目前为止一切顺利。
但是,E(X2) > E(X)2 是绝对必要的,因为浮点精度不是总是如此。在伪代码中,问题是这样的:
int count;
double sum, sumOfSquares;
...
double value = <current-value>;
double sqrVal = value*value;
count++;
sum += value; //slightly rounded down since value is truncated to fit into sum
sumOfSquares += sqrVal; //rounded down MORE since the order-of-magnitude
//difference between sqrVal and sumOfSquares is twice that between value and sum;
对于可变序列,这不是一个大问题 - 您最终会稍微低估方差,但这通常不是一个大问题。但是,对于具有非零均值的常数或几乎常数集,它可能意味着 E(X2) 2,导致计算出的方差为负,这违反了使用代码的预期。
现在,我知道了 Kahan Summation,这不是一个有吸引力的解决方案。首先,它使代码容易受到优化变幻莫测的影响(取决于优化标志,代码可能会或可能不会出现此问题),其次,由于精度,问题不是真的 - 这很好够了 - 这是因为加法将 systematic 错误引入零。如果我可以执行该行
sumOfSquares += sqrVal;
以确保 sqrVal 向上而不是向下舍入到 sumOfSquares 的精度的方式,我会有一个数值上合理的解决方案。但是我怎样才能做到这一点呢?
编辑:已完成的问题 - 为什么在标签字段的下拉列表中按 enter 无论如何都会提交问题?
【问题讨论】:
-
对集合进行排序并从较小的值开始进行相同的计算会改变这种情况吗?
-
对于当前解决方案,排序的计算成本要高得多,需要 O(n log(n)) 时间和 O(n) 存储,而不是线性时间(以及低得多的常数因子)和常数存储。我处理的数据集任意大(越大越好),所以这是个问题。
-
毫无疑问,但这首先会有所帮助吗?
-
这无济于事,因为即使在完全恒定的集合中也会出现问题 - 只要平方和和总和有差异截断错误(它们会),不幸的系列可能会导致负方差。
标签: c# c++ floating-point ieee-754