C++ 如何使用double更安全、更精确?
每种情况下的语句在数学上是等价的。我的问题是编码时选择哪一个更好。代码的哪一部分可能会导致某些变量范围溢出,而另一部分不会导致相同范围的溢出。代码的哪一部分更精确,为什么C++ 如何使用double更安全、更精确?,c++,c,double,overflow,underflow,C++,C,Double,Overflow,Underflow,每种情况下的语句在数学上是等价的。我的问题是编码时选择哪一个更好。代码的哪一部分可能会导致某些变量范围溢出,而另一部分不会导致相同范围的溢出。代码的哪一部分更精确,为什么 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; 情况1:x*=y*z类似于x=x*(y*z)因此本例强调求值顺序。
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;
情况1:x*=y*z
类似于x=x*(y*z)代码>因此本例强调求值顺序。如果子产品超出计算范围并转换为INF
或0.0
或次正常值,则最终产品将受到显著影响,具体取决于顺序。OTOH,中级数学可以在更宽的FP类型下执行。搜索FLT\u EVAL\u方法
。在这种情况下,如果所有计算都按长双精度进行,则顺序可能不相关
案例2:两种形式略有不同。第二个在数值上更稳定,因为加法/减法使用精确值:1,y
与第一个x,x*y
,x*y
可能是一个四舍五入的答案。在这种情况下,当y
接近-1.0
时,附加/减法容易导致严重的精度损失。作为案例1,更广泛的中级数学有帮助,但第二种形式更好
C11(C99?)提供fma(双x,双y,双z)
,使用fma(x,y,x)
将是另一个很好的选择
fma
函数计算(x×y)+z
,四舍五入为一个三元运算:它们计算值(好像)到无限精度,并根据当前四舍五入模式四舍五入一次到结果格式。可能发生范围错误
案例3:
这里的“技巧”是双0.2
与数学0.2相同?通常情况下,情况并非如此——但它们关系密切。然而,优化编译可以1)将它们视为相同的或2)或者与案例1一样,使用更广泛的数学。那么这两行代码的结果是相同的
否则:根据舍入模式,这两种形式的最小位(ULP)可能会出现差异。对于较弱的编译器,建议使用/5.0
除以5.0比乘以0.2更准确。但是无论哪种编码方式,智能编译器都可以使用它来对两者进行广泛的乘法。您应该阅读有关浮点算术的知识。通过简单的搜索可以找到足够的信息。如果您的项目需要通过强制执行某种编码样式来最大化浮点精度,那么可能会出现问题x/5.0
会更精确,因为0.2不能用二进制浮点表示,但是x*0.2
会更快“…由于溢出和下溢问题而安全”除非您知道要处理的数字有多大,否则这没有意义。您的意思是询问哪些行可能存在浮点不准确问题吗?例如,(0.000001*100000000)*10000000
不会导致我的计算机溢出,而0.000001*(100000000*10000000)
会导致溢出。另外,我想考虑浮点的不准确性。我不明白为什么你说<代码> x*(1 +y)< /> >比<代码> x+x*y > <代码>第一个使用近似值<代码> x*y 和<代码> x>代码>,而第二个使用近似值<代码> x<代码>,<代码> y>代码>1.0+y
在我的GCC 4.9.2中,当我选择变量x=1000000
和y=0.001
时,结果是x*(1.0+y)=10000999.999…
和x+x*y=1001000
@Mehrshadx
不是一个近似值,它是方程的一个参数。对于误差分析,假设方程/函数的参数是精确的。乘积x*y
不是参数,而是可能不精确的中间结果。加法/减法比乘法/除法对不精确参数更敏感。因此,首先使用精确增广法执行+
,结果比先执行*
更精确。@Mehrshad尝试了您的测试值,并且使用各种代码,这两种方法彼此之间的距离在1.0 ULP以内。在y接近-1.0的情况下尝试了相同的测试,x*(1.0+y)
仍然接近正确答案,而x+x*y
失去了显著的精确度–在y=-1+1e6
的情况下失去了6位小数位数的精确度。这不是哪种方法最常给出“正确”答案的问题。对于大多数数字,两者的作用大致相同。但是y接近-1.0
,x+x*y
给出了显著的较差结果x*(1.0+y)
没有这样的孔。概括:最大进动损失包括加法/减法,其中结果几乎抵消。最大范围问题发生在同一绝对方向上的乘法/除法链朝向0或INF
。
// 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;