【问题标题】:IEEE 754: Javascript vs CIEEE 754:Javascript 与 C
【发布时间】:2012-07-03 09:42:22
【问题描述】:

据我了解,大名鼎鼎的

 (0.1 + 0.2) !== 0.3

gotcha 实际上并不是 Javascript 的错。这正是 IEEE 754 的工作方式。类似的输出出现在 Python 中,它也遵循 IEEE 754 规则。

那么这个特定的例子是如何在 C 中按预期工作的,有时。如果我直接比较

printf("%d\n", (0.1+0.2) == 0.3);

我得到了(未?)预期的输出 0,但如果我将这些值放入变量中或打印出来,我会得到正确四舍五入的答案。

C Runnable Example

IEEE 754 的 C 实现是否做了额外的事情?还是我完全错过了其他东西。

更新

我发布的代码示例由于拼写错误而损坏。试试这个 Fixed C Runnable Example

但原来的问题仍然存在。

double d1, d2, d3;
d1 = 0.1;    d2 = 0.2;    d3 = d1 + d2;
printf ("%d\n", ((((double)0.1)+((double)0.2)) == ((double)d3)));
printf ("%.17f\n", d1+d2);
printf ("%d\n", ((d1+d2) == d3));

输出是

1
0.30000000000000004
1

现在改写的问题是:

  1. C 编译器为什么(以及何时以及如何)冒昧地这么说

    0.3 == 0.30000000000000004

  2. 鉴于所有事实,C 实现而不是 Javascript 的实现不正确吗?

【问题讨论】:

  • 在您的代码示例中,您实际上从未打印任何非整数的数字。
  • 你的问题与 JavaScript 没有任何关系。它与 C 和双打处理有关,有时你会得到一个结果,而其他时候你会得到不同的结果。 (我的猜测是,有时,预处理器正在做数学运算,所以编译器看到的是printf("%d\n", 0.3 == 0.3);,但这只是一个猜测。)
  • @Manav:即使您还提供了链接,也请始终在问题本身中包含相关代码(和输出)。您当前在问题中拥有的代码与您链接到的代码不同,并且链接可能会失效。更多:meta.stackexchange.com/questions/118392/…
  • @Alnitak 所以?这有关系吗?
  • 请注意,GCC 与 MSVC 的“破坏方式不同”。查看不同的编译器如何给出不同的结果:stackoverflow.com/questions/10912128/…

标签: javascript c floating-point ieee-754


【解决方案1】:

C 编译器为什么(以及何时以及如何)冒昧地这么说

    0.3 == 0.30000000000000004

考虑到所有事实,C 实现被破坏不是真的,而不是 Javascripts'?

不是。

给出的输出来自这段代码:

printf ("%d\n", ((((double)0.1)+((double)0.2)) == ((double)d3)));

但你写道:

d1 = 0.1;    d2 = 0.2;    d3 = d1 + d2;

所以d3 不是 0.3,它是0.30000000000000004

【讨论】:

  • 所以没有人被打破。只是可怜的 o'l Javascript 名声不好:)
  • @Manav 甚至不是那样 - 人们只是不了解 IEEE 754。
  • 如果您更改为d3 = 0.3,那么您的printf ("%d\n", d1+d2==d3); 将再次变为false。
  • @Agent_L 这正是我的意思 - 目前他正在比较 0.1 + 0.2 == 0.1 + 0.2不是 0.1 + 0.2 == 0.3
【解决方案2】:
`printf("%d\n", (0.1+0.2) == 0.3);`

这些是 DOUBLEs 而不是 FLOATs。

做:

printf("%d\n", (0.1+0.2) == 0.3);
printf("%d\n", (0.1f+0.2f) == 0.3f);

瞧!

http://codepad.org/VF9svjxY

Output:
0
1

看看这个问题。这是关于其他的,但是使用 GCC 的 ppl 得到的结果与使用 MSVC 的 ppl 不同。 printing the integral part of a floating point number

【讨论】:

  • MSVC 在 64 位模式下使用 SSE FPU,在 32 位模式下使用 x87 FPU。后者以 80 位精度存储所有中间结果。可以通过中间存储和重新加载来解决这个问题。
【解决方案3】:

您的示例代码使用 1、2 和 (1+2),它们都可以用双精度浮点数精确表示。另一方面,0.1 不能完全用浮点数表示,因此你得到了这个数字的近似值。将大约 0.1 添加到大约 0.2 时,您将得到大约 0.3,但这是否与编译器在表示 0.3 时选择的近似值完全相同。

表示使用二进制。这意味着它使用 1/2, 1/4, 1/8, 1/16, 1/32(等等,以达到一定的精度......)。这意味着可以准确地表示 0.5、0.25 等。不是二进制分数的精确和的数字可以非常接近。

解决方案:不要相互比较浮点数。将它们的差异与您不关心的一些小数字进行比较。

#define EPSILON 0.000001
printf("%d", fabs((0.1+0.2) - 0.3 ) < EPSILON );

我不确定为什么 C 代码有效而 python/javascript 无效。这是魔法。但希望无论如何这都能回答您的问题。

【讨论】:

  • 我想知道你在答案的最后一行提到的这个“魔法”。
  • 我在他更新到新代码示例之前回答了。前一个坏了,根本不比较小数。所以我不知道为什么他认为他的 C 代码有效而 javascript 没有(特别是因为没有 javascript 代码示例)。所以我解释了浮点数是如何表示的以及为什么在算术中没有精确的答案,以及为什么一种实现可能有效而另一种不能。因此,比较浮点数是否相等是“神奇的”。即不要这样做。
猜你喜欢
  • 2013-06-12
  • 1970-01-01
  • 2015-04-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-20
相关资源
最近更新 更多