c – 浮点算术运算的精度是多少?

前端之家收集整理的这篇文章主要介绍了c – 浮点算术运算的精度是多少?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
考虑下面两个非常简单的乘法:
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;

默认情况下,乘法是否以高于操作数的精度完成?我的意思是,在第一次乘法的情况下,它是以双精度完成的,如果在x86架构中第二次,它是以80位扩展精度完成的,或者我们应该在表达式中将操作数转换为更高的精度,如下所示?

result1=(double)var1*(double)var2;
result2=(long double)var3*(long double)var4;

其他操作(添加,除法和余数)怎么样?例如,当添加两个以上的正单精度值时,如果用于保存表达式的中间结果,则使用双精度的额外有效位可以减少舍入误差.

解决方法

浮点计算的精度

C 11 incorporates在cfloat中从C99定义FLT_EVAL_METHOD.

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;

如果您的编译器将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;

如果没有这个转换(如果你写var3 * var4),如果编译器的FLT_EVAL_METHOD定义是0或1,那么产品将以double的精度计算,这是一种耻辱,因为它注定要存储在long double中.

如果编译器将FLT_EVAL_METHOD定义为2,那么(long double)var3 *(long double)var4中的转换不是必需的,但它们也不会受到任何影响:表达式表示有和没有它们完全相同.

Digression:如果目标格式与参数一样窄,那么中间结果的扩展精度何时更好?

矛盾的是,对于单个操作,最好只舍入一次目标精度.在扩展精度中计算单个乘法的唯一效果是,结果将四舍五入为扩展精度,然后为双精度.这使它成为less accurate.换句话说,使用FLT_EVAL_METHOD 0或1,由于双舍入,上面的结果r2有时不如r1准确,并且如果编译器使用IEEE 754浮点,则永远不会更好.

包含多个操作的较大表达式的情况不同.对于这些,通常更好的是通过显式转换或者因为编译器使用FLT_EVAL_METHOD == 2来计算扩展精度的中间结果.这个question及其接受的答案显示当使用80位扩展精度计算二进制64 IEEE时754参数和结果,插值公式u2 *(1.0 – u1)u1 * u3总是在u2和u3之间产生u1在0和1之间的结果.由于更大的舍入误差,这个属性可能不适用于二进制64精度中间计算然后.

猜你在找的C&C++相关文章