双重的准确性导致悲伤

时间:2013-06-13 01:33:44

标签: c std floating-point-precision

我有一个ascii“15605632.68128593”,我希望转换 在没有失去准确的情况下达到双倍 -

 so 
 double d;
 d=(double)atof("15605632.68128593");
 printf("%f",d);

 printed result is 15605632.681286

有什么想法吗?

谢谢 - !

3 个答案:

答案 0 :(得分:8)

很可能你没有得到所有尾随小数位。试试printf("%.8f", d)

您也可以尝试sscanf("15605632.68128593", "%lf", &d)代替atof来电。

也没有必要将atof的结果转换为double。它已经是一个双倍。但演员没有伤害。

请注意 - 至少大约6年前,当我仔细研究这个问题时 - 许多printfscanf实施都是错误的,因为它们不像你那样完美地反转。假设。 Visual C / C ++和gcc在其本机实现中都存在问题。 This paper是一个有用的参考。

Cygwin与gcc 4.3.4:

#include <stdio.h>

int main(void)
{
  double x;

  sscanf("15605632.68128593", "%lf", &x);
  printf("%.8f\n", x);
  return 0;
}

然后:

# gcc foo.c
# ./a
15605632.68128593

答案 1 :(得分:3)

目标:将“15605632.68128593”转换为双精度而不会失去准确性

atof()完成了程序可以做到的最好。但由于15605632.68128593(一个16位数字)在double完全无法表示为C,因此它近似为1.560563268128593080...e+07。因此,虽然损失很小,但准确性已经丧失。

Typical double可代表约2 64 个不同的数字。附近的候选人和OP的字符串如下所示,以供参考。

 15605632.68128592893...  previous double
"15605632.68128593"
 15605632.68128593080...  closest double

在尝试打印时出现了悲痛,认为打印的内容是x完全值。而是打印了一个舍入值。使用%f说明符默认为'。'右侧的6个位置。提供报告的15605632.681286,一个14位数字。

查看所有double的所有有效数字的更好方法是将%e格式与DBL_DIG一起使用。 DBL_DIG是'。'右边的最多位数,在十进制指数表示法 %e中,以显示“往返”a所需的所有数字double(字符串加倍到字符串,没有字符串差异)。由于%e始终在“。”左侧显示1位数字,因此下方的图片会显示1 + DBL_DIG个有效数字。 DBL_DIG在我的我和许多C环境中都是15,但它有所不同。

如果您希望显示所有有效数字,则需要确定重要的内容。 nextafter()函数显示下一个可表示的double。所以我们可能想要显示至少足够的数字来区分x和下一个x。我推荐DBL_DIG + 2。虽然DBL_DIG可能就足够了。 Details

程序用于"1.560563268128593e+07"完全值为15605632.68128593079745769500732421875。在某些情况下,您需要查看所有这些数字。即使您要求大量数字,在某些时候,printf()只会给你零。

#include <stdio.h>
#include <float.h>
#include <tgmath.h>

int main(int argc, char *argv[]) {
  double x;
  x = atof("15605632.68128593");
  printf("%.*le\n",DBL_DIG, x); // All digits "round-trip" string-to-double-string w/o loss
  printf("%.*le\n",DBL_DIG + 1, x);  // All the significant digit "one-way" double-string
  printf("%.*le\n",DBL_DIG + 1, nextafter(x, 2*x)); // The next representable double
  printf("%.*le\n",DBL_DIG + 3, x);  // What happens with a few more
  printf("%.*le\n",DBL_DIG + 30, x); // What happens if you are a bit loony
  return 0;
}

1.560563268128593e+07
1.5605632681285931e+07
1.5605632681285933e+07
1.560563268128593080e+07
1.560563268128593079745769500732421875000000000e+07

答案 2 :(得分:1)

double没那么精确。它只能将十进制字符串中的15(DBL_DIGfloat.h)往返到double回到十进制字符串。

编辑虽然一般来说,我的说法属实,但这似乎不是你的问题。虽然存在16位小数不能往返的数字,但这个特定的输入可以。