如何使用32位浮点数正确计算大量数字的运行平均值?

5
我正在编写一款路径追踪器,需要对每个像素采集大量样本并取平均值。在1024次和16384次采样中,我发现视觉效果存在显著差异;16384次采样的图像颜色较暗。我猜测这是因为16384次采样的图像出现了浮点精度误差。我通过将每个值除以16384来计算颜色值的平均值,然后将它们相加。

有没有办法在最小化舍入误差的同时,对一个已知数量级的大量难以计算的数字进行平均?最好不需要非恒定的内存,并且绝对不会丢失任何样本。


正确且浮点?我很好奇这是否会得到比显而易见的“不兼容的概念”更有趣的答案。 - Piskvor left the building
1
我使用float是因为我试图在聚合器数组上节省内存(是的,有很多浮点数在一个数组中,我非常努力地尝试将其至少保留在L2中)。 问题不在于获得“正确”的答案,而在于如何获得“最佳”准确性。 - FeepingCreature
1
一个明显的实验是在测试运行中切换到双精度。如果这样做不能消除变暗,那么它很可能不是首要的精度问题。 - hmakholm left over Monica
聚合器数组?你所要做的就是将它们加起来放在一个变量(“变量”,单数)中,这个变量比你用于存储样本的类型要大得多。你正在尽力避免使用一些额外的字节。 - Beta
聚合器数组,因为我会批量计算光线。 :-) - FeepingCreature
2个回答

7
您可能需要使用Kahan求和算法。这是一种简单而有效的方式,可以在使用有限精度浮点运算对大量数据进行求和时,最小化累积舍入误差。

7
但由于OP已经出于空间原因使用单精度浮点数,切换到双精度将比单精度Kahan算法提供更好的精度,而且所需付出的代价要小得多。Kahan算法基本上使用两个寄存器累加器将有效位数加倍——但是双精度的本地尾数已经比单精度的宽超过两倍了(因为指数只表示一次)。 - hmakholm left over Monica
Kahan很适合,因为我的结果数组中恰好有一个vec3f我目前没有使用(发光颜色和反射颜色)。所以这很好。 (另一种选择是使用联合,但这些有点丑陋) - FeepingCreature

1

由于您正在除以2的幂,并且您的数字不是超小型的,因此此步骤不应对结果的准确性产生任何影响。您只是从指数中减去14。

重要的是样本中的位数。

浮点数给您24位精度。如果您有2 ^ 14 = 16384个样本,则当您将它们全部加起来时,您将逐渐失去精度,直到某个时刻在第24-14 = 10位之后的任何内容都会丢失。换句话说:在那一点上,您只保留了约3个小数位。

是否可能使用int作为累加器,甚至是uint?这样,您将保留8个额外的位,比1024和16384个样本之间的差异多两倍。

还有第二种完全不同的选择。我不知道您的样本范围是什么,但如果它们大小相似,您可以从每个值中减去近似平均值,平均差异,并在最后再添加近似平均值。

通过此方法获得的收益取决于您对平均值的初始估计有多好,以及值与平均值的接近程度。因此,我认为在您的情况下它可能不太可靠。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接