浮点数除法和乘法的精度差异

9

这两者有什么区别:

average = (x1+x2)/2;
deviation1 = x1 -average;
deviation2 = x2 -average;
variance = deviation1*deviation1 + deviation2*deviation2;

还有这个:

average2 = (x1+x2);
deviation1 = 2*x1 -average2;
deviation2 = 2*x2 -average2;
variance = (deviation1*deviation1 + deviation2*deviation2) / 4;

请注意,在第二个版本中,我尽可能延迟除法。第二个版本[延迟除法]是否会增加精度?
上面的代码片段仅作为示例,我并不试图优化这个特定的代码片段。
顺便说一下,我询问的是除法的一般情况,而不仅仅是2或2的幂次方,因为它们在IEEE 754表示中转换为简单的移位。我选择除以2,只是为了用一个非常简单的例子来说明问题。

1
你为什么想要拖延它?你希望从中获得什么? - paxdiablo
@paxdiablo - 这正是问题所在 - 我是否通过延迟它获得精度(而不是性能)。 - Fakrudeen
实际上,你应该询问准确度而不是精度,但答案仍然相同。 - David Heffernan
@David - 是的 - 我确实是指准确性 - 感谢您的纠正。 - Fakrudeen
通常情况下(我需要再考虑一下)可能会有影响,但使用2的幂时,在分母不是子规范化的(极小)数的情况下,除法不会失去精度。因此在您的情况下,这绝对没有差别。您还可以乘以0.5或0.25而不是除以2或4;结果始终完全相同(因为2的幂是精确的)。 - R.. GitHub STOP HELPING ICE
显示剩余2条评论
4个回答

4

这没有什么收获。你只是改变了比例,但在计算中并没有得到更多的有效数字。

维基百科关于方差的文章以高层次的方式解释了一些稳健计算方差的选项。


重点是我尽可能地推迟除法操作。你能引用一些参考资料来证明它们是相同的吗?我知道《每个计算机科学家都应该了解的浮点运算知识》这篇文章,但它并没有涉及到这个具体问题。 - Fakrudeen
但是第一个中会有6个舍入,而第二个只有2个?我应该尽可能减少舍入吗?第一个:1'/',2'-',2'*'和1'+'. 第二个:1'/'和1个转换。假设x1和x2是整数。 - Priyank Bhatnagar
@logic 你在说什么呢?1. 这个问题是关于浮点数的。2. 用整型数据类型是无法进行四舍五入的。 - David Heffernan
@David:如果x1和x2是整数,则avg、d1和d2在第一种情况下为浮点数,在第二种情况下为整数?我从未说过要使用整数四舍五入。 - Priyank Bhatnagar
1
除法只是移动比例。方差计算的真正问题在于减法中的抵消。 - David Heffernan
显示剩余2条评论

2
您不会从中获得精度,因为IEEE754(可能是您在底层使用的技术)在任何规模下都给出相同的精度(位数)。例如,3.14159 x 107 和 3.14159 x 1010 的精度相同。
唯一可能的优势(前者)是当设置偏差时可以避免溢出。但只要值本身小于最大可能值的一半,这就不会成为问题。

2
@David,不,我指的是值的精度。精度是IEEE754中的一个特定术语,意思是有效数字中的位数加一(对于隐含位)。准确性是一个更加模糊的概念,因为具有更高比例和相同精度的值可能不够准确(如果您认为准确性是数字与其精确值之间的接近程度)。 - paxdiablo
1
我明显不理解你的论点。我不明白为什么精度是相关的。重要的是你离真实答案有多近,而不是你在尾数中有多少位。 - David Heffernan
@David,如果你的真实值需要比精度中可用的位数更多的位数,那么使用IEEE754就无法准确表示。我的回答是指出,只要不超出范围或在中间结果中失去精度,无论是先除后乘还是先乘后除都没有区别。精度相同,因此最终结果也相同。除以二的幂是对指数的简单调整,因此不会改变有效数字,这就是我说它不会失去精度的原因。因此,你只需要关注比例尺即可。 - paxdiablo
1
是的,我知道所有这些。我的答案说的也是一样的,但我不会如此随意地使用“精度”这个术语。所有值都具有相同的精度。随机值将具有与最接近真实值的表示相同的精度。 - David Heffernan
1
我知道精度是什么。我知道你并没有捏造它。但这不是关键点。如果问题是找到一种能得出接近真实值的答案的方法,那么精度就不重要了。所有答案都具有相同的精度。 - David Heffernan
显示剩余6条评论

1
回答你的问题最好的方法是运行测试(随机分布和基于范围?),看看二进制表示中的结果数字是否有所不同。
请注意,如果您这样做,您将面临一个问题,即由于编码平均值的方式,您的函数将无法处理值> MAX_INT/2
avg = (x1+x2)/2        # clobbers numbers > MAX_INT/2
avg = 0.5*x1 + 0.5*x2  # no clobbering

除非您正在编写语言级库,否则这几乎肯定不是问题。如果您的大多数数字很小,那么可能根本没有关系?实际上,这可能不值得考虑,因为方差的值将超过MAX_INT,因为它是一个平方量; 我想你可能希望使用标准差,但没有人这样做。

在这里,我在Python中进行一些实验(我认为通过委托数学计算给C库,Python支持IEEE​​什么东西...):

>>> def compare(numer, denom):
...     assert ((numer/denom)*2).hex()==((2*numer)/denom).hex()

>>> [compare(a,b) for a,b in product(range(1,100),range(1,100))]

没问题,我认为因为二进制中的2很容易表示成乘法和除法。但是,试试用3进行乘法和除法:

>>> def compare(numer, denom):
...     assert ((numer/denom)*3).hex()==((3*numer)/denom).hex(), '...'

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in <listcomp>
  File "<stdin>", line 2, in compare
AssertionError: 0x1.3333333333334p-1!=0x1.3333333333333p-1

这个可能很重要吗?也许如果你正在处理非常小的数字(在这种情况下,您可能希望使用对数算术)。然而,如果您正在处理大量数字(在概率中不常见)并且延迟除法,那么就会像我提到的那样面临溢出的风险,甚至更糟的是,由于难以阅读的代码而导致错误的风险


你真的在声称使用随机数据进行模拟是分析算法数值稳定性的最佳方法吗? - David Heffernan
那么这是做什么的最佳方式? - David Heffernan

1

我必须同意David Heffernan的观点,这不会给你更高的精度。

原因在于浮点值的存储方式。你有一些位表示有效数字,还有一些位表示指数(例如3.1714x10-12)。无论你的数字有多大,有效数字的位始终相同 - 这意味着最终结果实际上并不不同。

更糟糕的是,延迟除法可能会导致溢出,如果您有非常大的数字。

如果您真的需要更高的精度,有很多库允许使用大数字或高精度数字。


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