4

考虑以下代码:

$result *= $oldFactor / $newFactor;  //using shorthand *= operator

实际上是这样的:

$result = $oldFactor / $newFactor * $result;  //division done first

我也可以手动写成这样:

$result = $result * $oldFactor / $newFactor;  //multiplication done first

我的印象是乘法是一种更简单的运算,与除法运算相比,它不会受到舍入误差的影响。

另外,我有这种感觉,对于大多数日常人类数字,乘法运算会在除法之前产生一个“更大的数字”(假设使用的数字通常大于 1)。并且被划分后的较大数字在数值上更稳定。示例 .. 考虑5 * 7 / 2.3第一个操作 (mult) 的精确位置,因为这些数字以二进制精确表示。然后除法就完成了,它就像我们要得到的一样精确。但是考虑一下7 / 2.3 * 5,第一个操作是除法,我们已经产生了一个不能用二进制精确表示的数字,下一个操作(mult)通过乘法夸大了任何不精确性。

我的问题基本上是……这有关系吗?首先使用除法时我确实会失去精度,还是我完全安全地使用最适合我的任何操作顺序并且我会得到相同的结果?

4

1 回答 1

1

如果您的平台、工具链和代码符合IEEE-754算术模型标准实现,那么乘法和除法都会产生最多半个 ulp(最后一位的单位)错误的结果,假设舍入到最接近的模式,并且假设输入是准确的。所以,原则上没有区别。

但当然也有微妙之处。正如 Raymond Chen 的评论中提到的,这可能取决于您正在使用的价值观。首先,处理大数时 1/2 ulp 误差比处理小数时大,因此使用相同的输入进行 1 除法和 1 乘法运算,除法结果的误差当然会小于乘法。

一个1乘1除的序列,并使用一个显着精度的类型 as double,两种情况下的上限误差应该最多为 1 ulp,并且大多数情况下操作数应用顺序的差异应该很小。在评论的例子中,这种效果非常明显,因为机器有一位有效数字。

显然,有一些特殊性。一是使用浮点数的算术运算缺乏交换性。因此,执行 (*,/) 或 (/,*) 可能会给您带来非常细微的不同结果,例如在浮点块的边界处(一个结果在浮点指数块的顶部,另一个就在上面,在下一个浮点指数块的底部),因此其中一个结果的误差将是另一个结果的两倍。此外,可能存在上溢或下溢。因此,如果首先将两个大数相乘,它可能会溢出到inf这会污染进一步的操作。当首先除非常小的数字时,您可能会得到下溢,即数字结果为零,而它本来应该是非零的,或者甚至会得到一个次正规的结果,它的位数减少了,因此数字结果更大了相对误差。

在所有情况下,如果您关心操作很少的序列上的微小错误,或者您的计算中有很多操作的序列,那么您应该进行错误分析,先验后验(运行错误分析)。

编辑:根据对此答案的评论,我称之为指数块的内容实际上称为binade,即具有相同指数的浮点数集。

于 2022-01-13T07:53:25.083 回答