1

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

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;
4

1 回答 1

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或低于正常值,最终产品将根据订单受到显着影响。OTOH,可以在更广泛的 FP 类型上执行中级数学。搜索FLT_EVAL_METHOD。在这种情况下,如果所有计算都按long double.

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

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

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

案例3:

这里的“技巧”double 0.2和数学0.2一样吗?通常它不是——但它们很接近。然而,优化编译可以 1)将它们视为相同或 2)或在情况 1 中,使用更广泛的数学。那么两行代码的结果是相同的。

否则:根据舍入模式,这两种形式可能会在最低位 (ULP) 中表现出差异。使用弱编译器,推荐/5.0

除以 5.0乘以大约 0.2 更准确。但是无论以哪种方式编码,智能编译器都可以使用两者进行宽乘。

于 2015-10-08T14:08:28.650 回答