【问题标题】:Difference in accuracy with floating point division vs multiplication浮点除法与乘法的精度差异
【发布时间】:2011-09-22 21:12:54
【问题描述】:

这有区别吗:

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;

请注意,在第二个版本中,我试图尽可能晚地延迟除法。第二个版本[延迟划分]总体上会提高准确性吗?

上面的片段只是一个例子,我并不是要优化这个特定的sn-p。

顺便说一句,我问的是一般的除法,而不仅仅是 2 或 2 的幂,因为它们简化为 IEEE 754 表示的简单转换。我将除以 2,只是为了用一个非常简单的例子来说明这个问题。

【问题讨论】:

  • 为什么你要拖延它?您希望从中获得什么?
  • @paxdiablo - 这是一个确切的问题 - 我是否通过延迟来获得精度(而不是性能)。
  • 其实你应该问的是准确度而不是精确度,但答案还是一样
  • 这与语言无关吗?您不应该担心发生类型更改的位置吗?它们是整数、浮点数还是字符串?它们什么时候变成别的东西了?
  • @David - 是的 - 我的意思是准确 - 感谢您的更正。

标签: algorithm language-agnostic numbers floating-point


【解决方案1】:

没有任何收获。您只是在更改比例,但在计算中不会得到任何更重要的数字。

Wikipedia article on variance 在高层次上以稳健的方式解释了一些计算方差的选项。

【讨论】:

  • 重点是我将分工推迟到尽可能晚。您可以参考为什么它们相同吗?我知道“每个计算机科学家都应该知道的关于浮点运算的知识”——但它并没有谈论这个确切的事情。
  • 除法有什么特别之处?这与乘法相同。您将除以 2 替换为乘以 2。您需要担心的是加法和减法。
  • 但是第一轮不是有 6 个,第二轮只有 2 个吗?我应该尽量减少四舍五入吗?第一个:1'/',2'-',2'*' 和 1'+'。第二:1'/'和1个转换。假设 x1 和 x2 是整数。
  • @logic 你在说什么。 1.问题是关于浮点数的。 2. 你不能用 int 取舍。
  • 除法只是改变比例。方差计算的真正问题是从减法中取消。
【解决方案2】:

您不会从中获得精度,因为 IEEE754(可能您在幕后使用的东西)在您工作的任何规模下都能为您提供相同的精度(位数)。例如 3.14159 x 107 将与 3.14159 x 1010 一样精确。

唯一的可能优点(前者的)是您可以在设置偏差时避免溢出。但是,只要这些值本身小于可能最大值的一半,那将不是问题。

【讨论】:

  • 我认为您的意思是准确性而不是精确度。
  • @David,不,我的意思是精确度,就像值的精确度一样。精度是一个特定的 IEEE754 术语,表示有效数字中的位数加一(对于隐含位)。精度是一个更无定形的概念,因为具有更高比例和相同精度的值可能不太准确(如果您认为精度是一个数字与其精确值的接近程度)。
  • 我显然不明白你的论点。我不明白为什么精度是相关的。重要的是您与真实答案的接近程度,而不是您的有效数字中有多少位。
  • 是的,我都知道。我的回答是一样的,但我并没有这么松散地使用精度这个词。所有值都具有相同的精度。随机值将与最接近真实值的表示具有相同的精度。
  • 我知道什么是精度。我知道你没有编造。这不是重点。如果问题是关于找到一种方法,其结果接近真实值,那么精度就无关紧要了。所有答案都具有相同的精度。
【解决方案3】:

回答您的问题的最佳方法是运行测试(随机分布的和基于范围的?)并查看生成的数字在二进制表示中是否完全不同。

请注意,如果您这样做,您将遇到的一个问题是您的函数将无法为价值 > MAX_INT/2 工作,因为您的编码方式平均。

avg = (x1+x2)/2        # clobbers numbers > MAX_INT/2
avg = 0.5*x1 + 0.5*x2  # no clobbering

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

在这里,我在 python 中做了一些实验(我认为它支持 IEEE,因为它可能将数学委托给 C 库......):

>>> 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 和乘以 2 可以很好地表示为二进制。但是尝试乘以 3 和除以 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

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

【讨论】:

  • 你真的断言随机数据模拟是分析算法数值稳定性的最佳方法吗?
  • 所以这是最好的方法吗?
【解决方案4】:

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

原因是浮点值的存储方式。您有一些代表有效数字的位和一些代表指数的位(例如 3.1714x10-12)。无论您的数字有多大,有效数字的位总是相同的 - 这意味着最终结果不会真正不同。

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-05-06
    • 1970-01-01
    • 1970-01-01
    • 2022-11-02
    • 2011-12-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多