两个浮点数的除法给出了错误的答案

Division of two floats giving incorrect answer

尝试使用以下代码在 C 中划分两个浮点数:

#include <stdio.h>
#include <math.h>

int main(){
  float fpfd = 122.88e6;
  float flo = 10e10;
  float int_part, frac_part;

  int_part = (int)(flo/fpfd);
  frac_part = (flo/fpfd) - int_part;

  printf("\nInt_Part = %f\n", int_part);
  printf("Frac_Part = %f\n", frac_part);

  return(0);
}

对于这段代码,我使用命令:

>> gcc test_prog.c -o test_prog -lm
>> ./test_prog

然后我得到这个输出:

Int_Part = 813.000000
Frac_Part = 0.802063

现在看来,这个Frac_part似乎是不正确的。我先在计算器上尝试了相同的方程式,然后在 Wolfram Alpha 中尝试了相同的方程式,它们都给我:

Frac_Part = 0.802083

请注意小数点后第五位的数字不同。

这对大多数人来说似乎微不足道,但对于我正在做的计算来说却是至关重要的。

任何人都可以向我解释为什么 C 代码会出现此错误吗?

float 只有 6~9 位有效数字,对于实际中的大多数用途来说不够精确。将所有 float 变量更改为 double(提供 15~17 位有效数字)给出输出:

Int_Part = 813.000000
Frac_Part = 0.802083

当浮点运算的精度不足时,最自然的第一步就是使用精度更高的浮点类型,例如使用 double 而不是 float。 (正如其他答案中立即指出的那样。)

其次,检查不同的浮点运算并考虑它们的精度。对我来说最突出的一个错误来源是上面通过简单地转换为 int 和减法将 float 分为整数部分和小数部分的方法。这并不理想,因为当你从原始值中减去整数部分时,你正在做的算术运算涉及的三个数字(两个输入和结果)具有非常不同的比例,这可能会导致精度损失。

我建议使用 C <math.h> 函数 modf 而不是将浮点数拆分为整数和小数部分。 http://www.techonthenet.com/c_language/standard_library_functions/math_h/modf.php

(更详细地说:当你执行像 f - (int)f 这样的操作时,浮点加法程序将看到正在添加某些给定精度 X 的两个数字,并且它会自然地假设结果也会有精度X。然后它会在那个假设下进行实际计算,最后重新评估结果的精度。因为最初的预测结果并不理想,一些低位比特将得到丢了。)

Float 是浮点数的单精度,你应该尝试使用双精度,下面的代码给出了正确的结果:

#include <stdio.h>
#include <math.h>

int main(){
  double fpfd = 122.88e6;
  double flo = 10e10;
  double int_part, frac_part;

  int_part = (int)(flo/fpfd);
  frac_part = (flo/fpfd) - int_part;

  printf("\nInt_Part = %f\n", int_part);
  printf("Frac_Part = %f\n", frac_part);

  return(0);
}

为什么?

正如我所说,float 是单精度浮点数,它们比 double 小(在大多数体系结构中,sizeof(float) < sizeof(double))。 通过使用 double 而不是 float,您将有更多位来存储数字的尾数和指数部分(参见 wikipedia)。