预除分母时数值不稳定的风险是什么?

What is the risk of numerical instabilities when predividing denominators?

假设我想把一个数分成很多。

a /= x;
b /= x;
c /= x;
...

因为乘法更快,所以很想这样做

tmp = 1.0f / x;
a *= tmp;
b *= tmp;
c *= tmp;
...

1) 这能保证产生相同的答案吗?我怀疑不是,但一些确认会很好。

2) 如果 x 非常大或非常小,我预计这可能会导致准确性的显着损失。有没有一个公式可以告诉我我会牺牲多少准确性?

3) 也许没有方便的公式,但我们至少可以说明一个经验法则,说明何时会出现数值不稳定性问题?是操作数大小的关系,还是操作数大小的差异?

我的 50 美分:

1) 否,在 3 处进一步解释。

2) 我不知道任何公式,所以我将跳过这个。

3) 我知道的经验法则是尽量只在数量级相近的操作数之间执行运算。

实际样本:

您想将数字 63.000.000 除以 1.000.000。

使用第一种方法,您最终会将 63*10^6 除以 1*10^6,它们的量级非常接近。

但是,如果您使用第二种方法,那么

temp = 1.0f / x;

将产生 10^(-6)。

现在乘以63*10^6 * 10^(-6)会导致明显的精度损失,因为两者之间的量级差异很大。 CPU会尝试用10^6数的指数+分数表示来表示10^(-6)数...

一个可行的替代方案是将 temp 设为

temp = 1 / 1.000;

然后

a = a * temp * temp ;

因为星等会更近,精度损失的可能性更小。

当您使用 ieee-754 编号时,我想说您的方法是完全可以接受的。在 ieee-754 中 x 大致是

mantisa * 2 exponent

其中尾数是 1/2 到 1 之间的数字。

所以只要你只做乘法和除法,你当然会有精度损失,但这种损失与 x (*) 的大小无关,只与使用的浮点类型的精度(单精度、四精度的双精度,这意味着 floatdoublelong double,具体取决于编译器和体系结构)。

(*) 只有当你不会有下溢的溢出时,这才是正确的,对于 10300[ 的单精度,大约 1038 =29=] 用于双精度。

参考资料:维基百科上的第 Floating point and IEEE floating point

以下是一些带有支持链接的想法:

1) - 产生相同的结果?没有保证。对可变性的贡献太多了,一切都来自uP设计(记住math co-processor design error of the 486 DX math co-processor?) to compiler implementation, to the way a float is stored in hardware memory. (Good discussion on that here.

2) - 公式?我不知道一个。而且,显着 错误是什么意思?在任何情况下,您都可以通过以下方式建立对您将看到的精度的期望:

  • Understanding various implementations of floating point numbers (link compares 2)
  • What variable type is used (float, double, long double). (differences)
  • What architecture are you building on a 32 bit, or 64 bit, other?

关于浮点错误的讨论很多。 Here is one

3) 没有 真实的 经验法则(如果 经验法则你的意思是容易记住,容易应用,简单易懂)但是,这里是a good attempt 在回答有关浮点错误的问题时

1) 不,不保证产生相同的答案。即使使用 IEEE,通过使用 a/xa*(1/x).

,细微的舍入效果也可能导致 1 或 2 ULP 的不同

2) 如果 x 非常小(即比 DBL_MIN(最小归一化正浮点数)小一点,在次正规情况下),1/xINF,完全失去精度。 x 非常大时也会发生潜在的显着精度损失,例如当 FP 模型不支持次法线时。
通过针对最大的有限数 <= 1/DBL_MIN 和最小的非零数 >= 1/DBL_MAX 测试 |x|,代码可以确定何时开始出现明显的精度损失。公式可能取决于所使用的 FP 模型和 x 的指数以及模型的限制。在 binary64 的这个范围内,xEmin(或 Emax)的二进制指数的差异将是丢失的比特的一阶估计值。

3) 在上面讨论的范围内出现了显着的数值不稳定性。