【问题标题】:Why 0.1 + 0.2 == 0.3 in D?为什么 D 中有 0.1 + 0.2 == 0.3?
【发布时间】:2011-10-16 00:07:09
【问题描述】:
assert(0.1 + 0.2 != 0.3); // shall be true

是我最喜欢的检查语言是否使用本机浮点运算。

C++

#include <cstdio>

int main()
{
   printf("%d\n", (0.1 + 0.2 != 0.3));
   return 0;
}

输出:

1

http://ideone.com/ErBMd

Python

print(0.1 + 0.2 != 0.3)

输出:

True

http://ideone.com/TuKsd

其他示例

为什么这对 D 不成立?据了解,D 使用本机浮点数。这是一个错误吗?他们是否使用某些特定的数字表示?还有什么?相当混乱。

D

import std.stdio;

void main()
{
   writeln(0.1 + 0.2 != 0.3);
}

输出:

false

http://ideone.com/mX6zF


更新

感谢LukeH。这是there 中描述的浮点常量折叠的效果。

代码:

import std.stdio;

void main()
{
   writeln(0.1 + 0.2 != 0.3); // constant folding is done in real precision

   auto a = 0.1;
   auto b = 0.2;
   writeln(a + b != 0.3);     // standard calculation in double precision
}

输出:

false
true

http://ideone.com/z6ZLk

【问题讨论】:

  • 请将相关代码示例直接放在问题中,不要放在外部链接中。既要确保问题中的全部信息保留下来,又要使其更易于阅读。
  • 我本想反射性地点击关闭按钮,直到我注意到你写的是 == 而不是 !=
  • 关于您的更新:这不是编译器优化器的“问题”。这是合法的浮点行为,发生这种情况的可能性在 D 文档的"Floating Point Constant Folding" section 中进行了说明。
  • 请看看当你使用real类型而不是double类型时会发生什么:ideone.com/NAXkM
  • @Jean Hominal:真实类型的案例很有趣。想...

标签: floating-point floating-accuracy d constantfolding


【解决方案1】:

它可能正在优化为 (0.3 != 0.3)。这显然是错误的。检查优化设置,确保它们已关闭,然后重试。

【讨论】:

  • 等等,为什么编译器做十进制浮点计算,运行时做二进制浮点计算?
  • 好点。有趣的是,我刚试过这个,但我是假的;我自己无法重现 OP 的结果。虽然我正在编译为 32 位,但我想知道 64 位是否会有所作为。
  • 这是正确答案。请参阅d-programming-language.org/float.html 的“浮点常量折叠”部分。
  • 肯定有优化的东西。对变量进行了同样的尝试,结果为真:ideone.com/zO4OD
  • 嘿,我刚刚重新阅读了这个问题;我认为“D”是指该列表中的第四个示例;我试图在 C# 中重现它!
【解决方案2】:

根据我对D language specification 的解释,x86 上的浮点运算将在内部使用 80 位精度,而不是仅使用 64 位。

然而,人们必须检查这是否足以解释您观察到的结果。

【讨论】:

  • 哇,@Tomalak,我的头刚刚爆炸 ;-)
  • @Tomalak:与 0.2 和 0.3 一样——但使用 80 位精度而不是 64 位进行舍入会使值“相等”而不是不同。我刚刚检查了具有真实类型的变量,它再次评估为 false:ideone.com/sIFgk
【解决方案3】:

(Flynn 的答案是正确的答案。这个更普遍地解决了这个问题。)


您似乎在假设,OP,您的代码中的浮点不准确性是确定性的并且可能是错误的(在某种程度上,您的方法与那些不这样做的人的方法截然相反)还不懂浮点)。

虽然(正如 Ben 指出的)浮点不准确性确定性的,但从您的代码的角度来看,如果您在每一步都没有仔细考虑您的值会发生什么,情况并非如此。任何数量的因素都可能导致0.1 + 0.2 == 0.3 成功,编译时优化是一个,为这些文字调整值是另一个。

这里既不依赖成功也不依赖失败;不要依赖浮点等式任何一种方式

【讨论】:

  • 这是一个很好的观点——你不能依赖浮点运算来给你错误的答案! :-)
  • 浮点不准确确实会产生确定性、可预测的答案...只要您使用序列点和变量赋值来强制在每一步进行舍入。并且,请注意会消除舍入的编译器选项,例如应该使用 MSVC /fp:precise
  • 这是一个糟糕的解释。 IEEE 754 明确定义了包括+ 在内的基本操作。这里的问题是一种编程语言,而不是浮点数。此外,浮点相等是完美定义的。当它不是你想要的时候,你不应该使用它,仅此而已。
  • @Pascal:IEEE 754 可以。 D 没有。你断言“这里的问题是一种编程语言”,并且......你是对的!如果您仔细查看问题真的,您会发现它被标记为d,而不是IEEE 754。我真的希望这能帮助你理解这个问题。
  • @Ben:当然,如果你控制了所有这些因素。我的回答确实假定程序员没有这样做。我编辑了我对这个词的回答。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-11-04
  • 2020-10-24
  • 2020-02-15
  • 2012-01-26
  • 1970-01-01
  • 2013-03-10
  • 1970-01-01
相关资源
最近更新 更多