为什么 0.1 + 0.2 == 0.3 in D?

Why 0.1 + 0.2 == 0.3 in D?

提问人:Stas 提问时间:7/29/2011 最后编辑:StayOnTargetStas 更新时间:8/21/2018 访问量:18652

问:

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

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。这是此处描述的浮点常数折叠的效果。

法典:

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 常数折叠

评论

13赞 Anders Abel 7/29/2011
请将相关代码示例直接放在问题中,而不是在外部链接中。既要确保问题中的完整信息仍然存在,又要使其更易于阅读。
6赞 dan04 7/29/2011
我打算条件反射地单击关闭按钮,直到我注意到您写了而不是.==!=
2赞 LukeH 7/29/2011
关于您的更新:这不是编译器优化器的“问题”。这是合法的浮点行为,发生这种情况的可能性在 D 文档的“浮点常数折叠”部分进行了解释。
1赞 Jean Hominal 7/29/2011
请查看使用类型而不是类型时会发生什么:ideone.com/NAXkMrealdouble
0赞 Stas 7/29/2011
@Jean Hominal:真实类型的案例很有趣。思维。。。

答:

47赞 Flynn1179 7/29/2011 #1

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

评论

27赞 Jean Hominal 7/29/2011
等等,为什么编译器要做十进制浮点计算,运行时要做二进制浮点计算?
0赞 Flynn1179 7/29/2011
好点子。有趣的是,我刚刚尝试过这个,我得到了假的;我自己无法重现 OP 的结果。我正在编译为 32 位,我想知道 64 位是否有区别。
13赞 LukeH 7/29/2011
这是正确答案。请参阅 d-programming-language.org/float.html 的“浮点常量折叠”部分。
1赞 bezmax 7/29/2011
绝对是优化的东西。对变量进行了相同的尝试并得到了真实:ideone.com/zO4OD
3赞 Flynn1179 7/29/2011
呵呵,我只是重读了这个问题;我以为“D”是指该列表中的第四个示例;我试图在 C# 中重现它!
5赞 Jean Hominal 7/29/2011 #2

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

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

评论

2赞 Steve Morgan 7/29/2011
哇,@Tomalak,我的脑袋刚刚爆炸了;-)
2赞 Jean Hominal 7/29/2011
@Tomalak:0.2 和 0.3 也是如此 - 但以 80 位精度而不是 64 位四舍五入可能会使值“相等”而不是不同。我刚刚检查了具有真实类型的变量,它再次计算为 false:ideone.com/sIFgk
53赞 Lightness Races in Orbit 7/29/2011 #3

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


OP 你似乎在假设你的代码中的浮点不准确是确定性的,并且是可预测的错误(在某种程度上,你的方法与那些还不了解浮点的人截然相反)。

尽管(正如 Ben 所指出的)浮点不准确性确定性的,但从代码的角度来看,如果你没有非常慎重地考虑每一步的值发生了什么,情况就不是这样了。任何数量的因素都可能导致成功,编译时优化是其中之一,调整这些文字的值是另一回事。0.1 + 0.2 == 0.3

在这里既不依赖成功,也不依赖失败;无论哪种方式,都不要依赖浮点相等。

评论

25赞 Steve Morgan 7/29/2011
这是一个很好的观点——你不能依靠浮点运算给你错误的答案!:-)
8赞 Ben Voigt 7/30/2011
浮点不准确确实会产生确定的、可预测的答案......只要您使用序列点和对变量的赋值来强制在每一步进行舍入。并且,请注意将消除舍入的编译器选项,例如应使用 MSVC。/fp:precise
7赞 Pascal Cuoq 7/31/2011
这是一个可怕的解释。IEEE 754 明确定义了基本操作,包括 .这里的问题是编程语言的问题,而不是浮点问题。此外,浮点相等性也得到了完美的定义。当它不是你想要的时,你不应该使用它,仅此而已。+
0赞 Lightness Races in Orbit 8/1/2011
@Pascal:IEEE 754 确实如此。D 没有。你断言“这里的问题是一门编程语言”,并且......你是对的!如果你仔细观察这个问题,你会发现它被标记为 ,而不是 。我真的希望这能帮助你理解这个问题。dIEEE 754
0赞 Lightness Races in Orbit 8/1/2011
@Ben:当然,如果你控制了所有这些因素。我的回答确实假设程序员不会这样做。我把我的答案编辑得更好。