Poo*_*ria 7 c++ floating-point arithmetic-expressions rounding floating-point-precision
考虑下面两个非常简单的乘法:
double result1;
long double result2;
float var1=3.1;
float var2=6.789;
double var3=87.45;
double var4=234.987;
result1=var1*var2;
result2=var3*var4;
Run Code Online (Sandbox Code Playgroud)
默认情况下,乘法是否以高于操作数的精度完成?我的意思是,在第一次乘法的情况下,它是以双精度完成的,如果在x86架构中第二次,它是以80位扩展精度完成的,或者我们应该在表达式中将操作数转换为更高的精度,如下所示?
result1=(double)var1*(double)var2;
result2=(long double)var3*(long double)var4;
Run Code Online (Sandbox Code Playgroud)
其他操作(添加,除法和余数)怎么样?例如,当添加两个以上的正单精度值时,如果用于保存表达式的中间结果,则使用双精度的额外有效位可以减少舍入误差.
C++ 11 包含了FLT_EVAL_METHOD
C99 的定义cfloat
.
FLT_EVAL_METHOD Possible values: -1 undetermined 0 evaluate just to the range and precision of the type 1 evaluate float and double as double, and long double as long double. 2 evaluate all as long double
如果您的编译器定义FLT_EVAL_METHOD
为2,那么r1
和r2
,和s1
以及s2
之下的计算分别是等价的:
double var3 = …;
double var4 = …;
double r1 = var3 * var4;
double r2 = (long double)var3 * (long double)var4;
long double s1 = var3 * var4;
long double s2 = (long double)var3 * (long double)var4;
Run Code Online (Sandbox Code Playgroud)
如果您的编译器将FLT_EVAL_METHOD定义为2,那么在上面的所有四个计算中,乘法都是以long double
类型的精度完成的.
但是,如果编译器定义FLT_EVAL_METHOD
为0或1,则r1
和r2
,并且分别s1
和s2
,并不总是相同.计算时的乘法r1
和s1
以精度计算的乘法double
.计算时的乘法r2
和s2
以精度计算的乘法long double
.
如果您计算那些注定要被存储在一个更宽的结果类型比操作数的类型,因为是结果result1
和result2
你的问题,你应该总是在争论的宽度至少转换成类型为目标,为你做这里:
result2=(long double)var3*(long double)var4;
Run Code Online (Sandbox Code Playgroud)
如果没有这种转换(如果你写的话var3 * var4
),如果编译器的定义FLT_EVAL_METHOD
是0或1,那么产品将以精度计算double
,这是一种耻辱,因为它注定要存储在a中long double
.
如果编译器定义FLT_EVAL_METHOD
为2,那么转换(long double)var3*(long double)var4
不是必需的,但它们也不会受到任何影响:表达式表示使用和不使用它们时完全相同.
矛盾的是,对于单个操作,最好只舍入一次目标精度.在扩展精度中计算单个乘法的唯一效果是结果将四舍五入到扩展精度然后再double
精确到精度.这使得它不太准确.换句话说,对于FLT_EVAL_METHOD
0或1,r2
上面的结果有时不如r1
因为双舍入而准确,并且如果编译器使用IEEE 754浮点,则永远不会更好.
包含多个操作的较大表达式的情况不同.对于这些,通常通过显式转换或编译器使用,以扩展精度计算中间结果通常更好FLT_EVAL_METHOD == 2
.这个问题和其接受的回答表明,与对binary64 IEEE 754参数和结果的80位扩展精度中间计算的计算时,内插式u2 * (1.0 - u1) + u1 * u3
总是产生之间的结果u2
和u3
为u1
0和1之间该属性可以不保持binary64精密中间计算,因为更大的舍入误差.
浮点类型的常见算术转换在乘法、除法和模数之前应用:
\n\n\n\n\n通常的算术转换是对操作数执行并确定结果的类型。
\n\n\xc2\xa75.6 [expr.mul]
\n
类似地,对于加法和减法:
\n\n\n\n\n通常的算术转换是针对算术或枚举类型的操作数执行的。
\n\n\xc2\xa75.7 [expr.add]
\n
标准中对浮点类型的常见算术转换进行了如下规定:
\n\n\n\n\n许多期望算术或枚举类型操作数的二元运算符会以类似的方式导致转换并产生结果类型。目的是产生一个通用类型,这也是结果的类型。这种模式称为通常的算术转换,其定义如下:
\n\n[...]
\n\n\xe2\x80\x94 如果任一操作数的类型为
\n\nlong double
,则另一个操作数应转换为long double
。\xe2\x80\x94 否则,如果任一操作数为
\n\ndouble
,则另一个应转换为double
。\xe2\x80\x94 否则,如果任一操作数为
\n\nfloat
,则另一个应转换为float
。\xc2\xa75 [表达式]
\n
这些浮点类型的实际形式/精度是实现定义的:
\n\n\n\n该类型
\n\ndouble
提供的精度至少与 一样多float
,并且该类型long double
提供的精度至少与 一样多double
。该类型的值集float
是该类型的值集的子集double
;type 的值集double
是 type 的值集的子集long double
。浮点类型的值表示是实现定义的。\xc2\xa73.9.1 [基本.基础]
\n
归档时间: |
|
查看次数: |
1124 次 |
最近记录: |