如何使用双精度更安全,更精确

How to use double to be more secure and precise?

本文关键字:安全 双精度 何使用      更新时间:2023-10-16

两种情况下的语句在数学上是等价的。我的问题是,在编码时选择哪一个更好。哪一部分代码可能导致某些范围的变量溢出,而另一部分代码在相同范围内没有溢出。代码的哪一部分更精确,为什么?

double x, y, z;
//case 1
x = (x * y) * z;
x *= y * z;
//case 2
z = x + x*y;
z = x * ( 1.0 + y);
//case 3
y = x/5.0;
y = x*0.2;
// Case 1
x = (x * y) * z;
x *= y * z;
// Case 2
z = x + x*y;
z = x * ( 1.0 + y);
// Case 3
y = x/5.0;
y = x*0.2;

案例1:x *= y * z;x = x * (y * z);相似,因此该案例强调求值顺序。如果子产品超出计算范围,转化为INF0.0或亚正态,则根据顺序,最终产品将受到显著影响。此外,中级数学可以在更宽的FP类型中执行。搜索FLT_EVAL_METHOD。在这种情况下,如果所有计算都以long double方式完成,那么顺序可能无关。

案例2:这两种形式略有不同。第二个在数字上更稳定,因为加减法使用精确值:1, y与第一个x, x*y相比,x*y可能是一个四舍五入的答案。当y接近-1.0时,加法/减法容易造成严重的精度损失。与案例1一样,更广泛的中级数学有所帮助,但第二种形式仍然更好。

C11 (C99?)提供fma(double x, double y, double z),使用fma(x, y, x)将是另一个很好的选择。

fma函数计算(x × y) + z,作为一个三元运算进行四舍五入:它们计算值(好像)到无限精度,并根据当前的四舍五入模式将结果格式四舍五入一次。可能出现范围错误。

案例3:

这里的"技巧"是double 0.2与数学0.2相同?通常情况下还没有,但它们已经很接近了。然而,优化编译可以1)将它们视为相同或2)或像情况1一样,使用更广泛的数学。那么这两行代码的结果是一样的。

否则:根据舍入模式的不同,这两种形式的最小位(ULP)可能会有所不同。对于弱编译器,推荐使用/5.0

除以5.0 比乘以大约0.2更准确。但无论用哪种方式编码,聪明的编译器都可以对两者进行广泛的乘法运算。