避免浮点数的不准确性

时间:2017-12-21 15:09:02

标签: c floating-point numbers precision

我完全理解与浮点相关的问题,但我看到了一个非常有趣的行为,我无法解释。

float x = 1028.25478;
long int y = 102825478;
float z = y/(float)100000.0;
printf("x = %f ", x);
printf("z = %f",z);

输出结果为:

  

x = 1028.254761 z = 1028.254780

现在,如果浮动数字在我将其分配给变量x时未能表示该特定随机值(1028.25478)。为什么变量z的情况不一样?

P.S。我正在使用pellesC IDE来测试代码(C11编译器)。

3 个答案:

答案 0 :(得分:5)

我很确定这里发生的是后一个浮点变量被省略,而是保存在双精度寄存器中;然后作为printf的参数传递。然后编译器会认为在默认参数提升后以双精度传递此数字是安全的。

我设法使用GCC 7.2.0生成类似的结果,使用这些开关:

-Wall -Werror -ffast-math -m32 -funsafe-math-optimizations -fexcess-precision=fast -O3

输出

x = 1028.254761 z = 1028.254800

那里的数字略有不同^。

description for -fexcess-precision=fast说:

  

-fexcess-precision=style

     

此选项允许进一步控制超出精度              浮点运算以某种格式出现的机器              比IEEE标准和交换更精确或范围              浮点类型。默认情况下,-fexcess-precision=fast位于              影响;这意味着可以在更广泛的范围内进行操作              精度高于源中指定的类型              导致更快的代码,并且在四舍五入时是不可预测的              源代码中指定的类型发生。什么时候              如果指定了-fexcess-precision=standard,则编译C.              超精密遵循ISO C99规定的规则;在              特别是,强制转换和赋值都会导致值被舍入              他们的语义类型(而-ffloat-store只影响他们              分配)。如果a,则默认为C启用此选项[-fexcess-precision=standard]              使用严格一致性选项,例如-std=c99-ffast-math              默认情况下启用-fexcess-precision=fast,无论是否              使用严格的一致性选项。

此行为不符合C11

答案 1 :(得分:2)

将此限制为IEEE754严格浮点,答案相同。

1028.25478实际上是1028.2547607421875。这占x

y / (float)100000.0;的评估中,y被C的参数提升规则转换为float。距float 102825478最近的1028254801028.2547607421875。 IEEE754要求返回除法的最佳结果,该结果应为z1028.25480的值):与{{1}}最接近的数字。

所以我的回答与你观察到的行为不一致。我把它归结为你的编译器没有严格实现浮点数;或者可能没有实现IEEE754。

答案 2 :(得分:2)

代码就好像zdoubley/(float)100000.0y/100000.0

float x = 1028.25478;
long int y = 102825478;
double z = y/100000.0;

// output
x = 1028.254761 z = 1028.254780

一个重要的考虑因素是FLT_EVAL_METHOD。这允许选择浮点代码以更高的精度进行评估。

#include <float.h>
#include <stdio.h>
printf("FLT_EVAL_METHOD %d\n", FLT_EVAL_METHOD);
  

除了赋值和强制转换...之外,具有浮动操作数的运算符产生的值以及通常算术转换和浮动常量的值将被计算为范围和精度可能为的格式大于类型所要求的。评估格式的使用以实现定义的FLT_EVAL_METHOD

值为特征

-1不确定;
0仅根据范围和精度评估所有操作和常量 类型;
1评估...键入floatdouble double类型的范围和精度,评估long double ...到long double的范围和精度 类型;
2评估所有...到范围和精度 long double类型。

然而,适用于zfloat z = y/(float)100000.0;会失去分配的所有更高精度。

我同意@Antti Haapala代码使用的速度优化更少遵守预期的浮点数学规则。