简而言之:我如何执行a+b
使得由于截断而导致的任何精度损失远离零而不是接近零?
长篇大论
我正在计算一长串浮点值的总和,以计算集合的样本均值和方差。由于Var(X) = E(X 2 ) - E(X) 2,保持所有数字的运行计数、到目前为止所有数字的总和以及迄今为止所有数字的平方和就足够了。
到现在为止还挺好。
但是,绝对需要E(X 2 ) > 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(X 2 ) < E(X) 2,导致计算出的方差为负,这违反了使用代码的预期。
现在,我知道了 Kahan Summation,这不是一个有吸引力的解决方案。首先,它使代码容易受到优化变幻莫测的影响(取决于优化标志,代码可能会或可能不会出现这个问题),其次,问题并不是真正由于精度 - 这已经足够了 - 这是因为加法引入了系统误差趋于零。如果我能执行这条线
sumOfSquares += sqrVal;
为了确保 sqrVal 向上而不是向下舍入到 sumOfSquares 的精度,我会有一个数值上合理的解决方案。但我怎样才能做到这一点?
编辑:已完成的问题 - 为什么在标签字段的下拉列表中按 enter 无论如何都会提交问题?