如何检测由于浮点加法和乘法舍入而导致的精度损失?

问题描述

从计算机系统:程序员的角度来看:

具有单精度浮点数

  • 表达式(3.14f+1e10f)-1e10f的值为0.0:由于舍入而丢失了值3.14。

  • 表达式(1e20f*1e20f)*1e-20f的值为+∞,而1e20f*(1e20f*1e-20f)的表达式为1e20f

  • 如何检测由于浮点加法和乘法舍入而导致的精度损失?

  • 下溢与我描述的问题之间的关系和区别是什么?下溢是否仅是由于舍入而导致精度下降的特殊情况,将结果舍入为零?

谢谢。

解决方法

尽管在数学中,实数的加法和乘法是关联运算,但由于精度和范围有限,在浮点类型(例如float)上执行时,这些运算不是关联扩展名。

所以顺序很重要。

考虑示例,数字10000000003.14不能完全表示为32位float,因此(3.14f + 1e10f)的结果将与{{1 }},是最接近的可表示数字。当然,1e10f会改为产生3.14f + (1e10f - 1e10f)

请注意,我使用了3.14f后缀,因为在C中,表达式f包含(3.14+1e10)-1e10文字,因此结果的确是double(或更可能是像3.14999)。

在第二个示例中发生了类似的情况,其中3.14已经超出了1e20f * 1e20f的范围(但没有超出float的范围),并且连续的乘法是没有意义的,而{{1}在其他表达式中首先执行的}具有明确的结果(1),并且连续相乘产生正确的答案。

实际上,您采取了一些预防措施

  • 使用更广​​泛的类型。除非有其他要求,否则double最适合大多数应用。
  • 如果可能,请重新排序操作。例如,如果您必须添加许多术语,并且您知道一些术语小于其他术语,请先添加这些术语,然后再添加其他术语。避免相减相同数量级的数字。通常,评估代数表达式的方法可能比幼稚的方法更为准确(例如,用于多项式评估的霍纳法)。
  • 如果您对问题域有某种了解,那么您可能已经知道计算的哪一部分可能存在问题值,并在执行计算之前检查这些值是否大于(或小于)某些限制。
  • 尽快检查结果。当您已经有一个无穷大的数值或NaN时,继续进行计算是没有意义的;或者,如果您的目标值根本没有被修改,则继续进行迭代。