如何平均浮点数(2015)

2021-06-11 17:23:21

每一个现在,我最终不得不向怀疑的人解释为什么他们的节目如何对待他们摄取的数字。通过IEEE 754和双打,人们似乎认为可以毫不可热地添加一堆数字和平均值,并获得可靠的准确结果。

事实上,现在的事情比曾经20年前的事情更好。但是,给予足够的数字和足够的操作,仍然可以陷入有趣的不准确性。这是生活中的一个事实,而不是地球破碎的观察,但有时候,它确实需要一点努力向那些听到这个东西的人第一次听到这个东西,绝对确定他/她的编程实力。

为了说明这一点,我首先要从略微开始的例子开始,以便我们可以轻松,独立地计算预期的大小,并评估计算机计算的准确性。

也就是说,我们有十万个数字的向量。奇数索引元素相当大,甚至索引的元素相对较小。显然,这是一个创作的例子,但它有助于算像算法。

好吧,这肯定足够接近。毕竟,当您在五百百万升时,小数点后,谁关心十进制点后的第四个数字和第五位。需要一些浮点误差。等等等等等等。你以前听过这个。

因此,通过在交替顺序中添加大数字和少量数字,我们损失了一些准确性。

好吧,有足够的内存不仅仅是一个,而不是两个副本的原始数据集不是一个奢侈品可以始终负担得起,但是,在这种情况下,阵列足够小(我说在她右边没有人记住介意会称之为10万个元素的数组足够小)我们可以给它一个射击:

sub asc_sum {sum sort {$ a<> $ b} @_} sub asc_mean {asc_sum(@_)/ @_} printf"升序序列的平均值:%f \ n",asc_mean(@data);

对于匡威,让我们看看如果我们首先在降序排序数据时会发生什么:

子dsc_sum {sum sort {$ b< => $ a} @_}子dsc_mean {dsc_sum(@_)/ @_} printf"降序序列的平均值:%f \ n",dsc_mean(@data);

哎哟!误差现在比天真的平均值大35%,比增加序列的平均值大171%。

当然,正确的书面软件处理了这一点。要查看如何,请注意,第一个n个元素的平均值与第一个n + 1元素的平均值之间的差异简单

sub stable_mean {my $x̄= $ _ [0];对于我的$ i(1 .. $#_){$xī+ =($ _ [$ i] - $xī)/($ i + 1); $x̄; printf"稳定的平均值:%f \ n",stable_mean(@data); Printf"稳定的升序均值:%f \ n",stable_mean(排序{$ a => $ b} @data); Printf"稳定的下降均值:%f \ n",stable_mean(排序{$ b< $ gt; $ a} @data);

现在,在现实世界中,您有能够摄取未计入数量的数据的程序。它们的数字,划分它们,乘以它们,以“大数据”的名义对它们进行无法形容的事情。非常少数考虑自己C ++向导,或F#哲学家的人,或者C#ninjas实际上知道人们需要注意如何折磨数据。否则,在添加,划分,乘以,减去和提升到第n个电源的时间,您可能正在报告糊状,而不是数据。

一个节省现实世界的恩典是给定的变量不太可能包含具有如此极端范围的值。另一方面,在现实世界中,一个人几乎没有用一个变量工作,一个人几乎可以独立地验证各个求和的结果。

无论如何,这篇文章的重点是不是发表盛大的陈述,而是用一个简单的例子来说明,当使用浮点时,数字增加的方式,并分开。

该示例与编程语言无关。上面的我所谓的stable_mean是计算维基百科的差异的在线算法的一部分。我在三十年前接近地了解它,试图调试Fortran程序。直到我决定在写这篇文章时检查几件事,直到写这篇文章,我不知道它甚至有一个名字;-)

作为这种排序的危险的示例,请考虑以下简短示例:

#!/ usr / bin / env perl使用utf8;使用严格;使用警告;使用统计::描述性;我的$ k = 10_000_000_000;我的@d =(4,7,13,16);我的@x = map $ k + $ _,@d;我的$ stat =统计::描述性:full-> new; $ stat-> add_data(@x);我的$μ= $ stat->吝啬的;我的@s = map {($ _-$μ)** 2} @x; Printf"使用统计数据计算的差异::描述性:%f \ n",$ stat->方差; Printf"使用稳定均值计算的方差:%f \ n",(@s * stable_mean(@s))/(@s - 1); sub stable_mean {my $x̄= $ _ [0];对于我的$ i(1 .. $#_){$xī+ =($ _ [$ i] - $xī)/($ i + 1); $x̄; }