每种情况下的陈述在数学上都是等价的。我的问题是在编码时哪一个更好。代码的哪一部分可能会导致某些变量范围溢出,而另一部分代码的相同范围没有溢出。哪部分代码更精确,为什么?
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;
答案 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);
类似,因此本案例强调了评估顺序。如果任何子产品超出计算范围并转换为INF
或0.0
或低于正常值,则最终产品将受到严重影响,具体取决于订单。 OTOH,中间数学可以在更宽的FP类型下执行。搜索FLT_EVAL_METHOD
。在这种情况下,如果所有计算都以long double
完成,则订单可能无关紧要。
案例2: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更准确。但无论以哪种方式编码,智能编译器都可以使用两者进行广泛的乘法运算。