Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/68.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 如何使用double更安全、更精确?_C++_C_Double_Overflow_Underflow - Fatal编程技术网

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
@Mehrshad
x
不是一个近似值,它是方程的一个参数。对于误差分析,假设方程/函数的参数是精确的。乘积
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;