浮点计算的精度
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
and r2
,和的s1
and 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;
如果您的编译器将 FLT_EVAL_METHOD 定义为 2,则在上面的所有四个计算中,乘法将以以下精度完成:long double
type.
但是,如果编译器定义FLT_EVAL_METHOD
作为 0 或 1,r1
and r2
、 和 分别s1
and s2
,并不总是相同。计算时的乘法r1
and s1
的精度为double
。计算时的乘法r2
and s2
的精度为long double
.
从狭隘的论点中获得广泛的结果
如果您计算的结果注定要存储在比操作数类型更宽的结果类型中,如下所示result1
and result2
在您的问题中,您应该始终将参数转换为至少与目标一样宽的类型,如下所示:
result2=(long double)var3*(long double)var4;
没有这种转换(如果你写var3 * var4
),如果编译器的定义FLT_EVAL_METHOD
为 0 或 1,乘积将以以下精度计算double
,这是一种耻辱,因为它注定要存储在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
. This question其接受的答案表明,当使用 80 位扩展精度中间计算进行二进制 64 IEEE 754 参数和结果计算时,插值公式u2 * (1.0 - u1) + u1 * u3
总是产生介于u2
and u3
for u1
介于 0 和 1 之间。此属性可能不适用于二进制 64 精度中间计算,因为此时舍入误差较大。