C# .NET及更高版本:使用双重算术的最佳实践

C# .NET及更高版本:使用双重算术的最佳实践,c#,.net,math,double,C#,.net,Math,Double,我正在开发一个进行浮点计算的程序,我在.NET中偶然发现了一个有趣的舍入问题,根据这个表达式: 0.1 + 0.2 == 0.3 计算结果为false,因为: 0.1 + 0.2 计算结果为0.3000000000004,而不是0.3。这严重影响了单元测试 我确实理解为什么会发生这种情况,但我感兴趣的是:在处理双重算术时,为了尽可能避免此类问题,我应该遵循哪些最佳实践 编辑:使用十进制类型没有帮助 总结:感谢大家的评论。不幸的是,你们中的一些人认为这个问题是如何使0.1+0.2等于0.3,这

我正在开发一个进行浮点计算的程序,我在.NET中偶然发现了一个有趣的舍入问题,根据这个表达式:

0.1 + 0.2 == 0.3
计算结果为false,因为:

0.1 + 0.2
计算结果为
0.3000000000004
,而不是
0.3
。这严重影响了单元测试

我确实理解为什么会发生这种情况,但我感兴趣的是:在处理双重算术时,为了尽可能避免此类问题,我应该遵循哪些最佳实践

编辑:使用
十进制
类型没有帮助


总结:感谢大家的评论。不幸的是,你们中的一些人认为这个问题是如何使0.1+0.2等于0.3,这不是我想要的。我接受浮点运算可以返回有变化的值。我问的是,为了使这种变化不会引起问题,最佳做法是什么。我认为这个问题已经准备好结束了。

您通常会测试在一定公差范围内的平等性

例如,在中,您可以编写:

Assert.AreEqual(x, y, tol);
其中,
tol
是适当选择的公差值。其他单元测试框架将具有类似的浮点值断言功能


当然,如何选择公差可能是一个巨大的话题。简单地说,为了知道要使用什么公差,您需要了解有关测试中的计算的一些信息。将对该计算进行分析,以确定合适的公差。

当使用浮点类型时,使用某种类型的epsilon计算等式

与这个答案相比:


Decimal
是一个选项,但必须正确填写:
Decimal a=0.1M


((0.1M+0.2M)=0.3M)
计算
真值

你需要阅读两件事:

  • David Goldberg 1991年最优秀的论文[GOLDBERG91]。以下是摘要:

    浮点运算被许多人认为是一门深奥的学科。 这相当令人惊讶,因为浮点在计算机系统中无处不在: 几乎每种语言都有浮点数据类型;计算机从PC到 超级计算机有浮点加速器;大多数编译器将被调用 经常编译浮点算法;几乎每一个 操作系统必须响应浮点异常,如溢出。 本文提供了一个关于浮点运算方面的教程,这些方面有着直接的影响 对计算机系统设计者的影响。它从浮点背景开始 表示和舍入误差,继续讨论IEEE浮点数 点标准,并以计算机系统建设者如何 更好的支持浮点运算

    还有一些PDF格式的论文来源:

    如果你是ACM的成员,你可以

不要忘记这一令人遗憾地获得的浮点智慧宝库:

[GOLDBERG91]

戈德伯格,大卫,1991年。“一个简单但现实的浮点计算模型”,《ACM计算调查》,第23卷,第1期,1991年3月。第5-48页。

如果你期望
0.1+0.2==0.3
的计算结果为
true
使用
十进制
。不要测试精确的相等性。测试以查看结果是否在可接受的错误范围内,例如,
Assert(Math.Abs((0.1+0.2)-(0.3))<1e-10)
@GregS如果没有抓住要点。测试中的计算使用二进制浮点执行。这是设计的。@DavidHeffernan如果文字是小数,它就工作:
((0.1M+0.2M)=0.3M)
计算true@ZoolWay小数可能适用于所讨论的确切代码,但这并不能解决OP的一般问题<代码>(1m/3m)*3m==1m→
false
Decimal在被测代码使用二进制浮点时不是选项。是的,当您已经获得的值是浮点时,Decimal确实不是选项。但这不是问题,只是如何处理浮动比较的最佳实践。最佳实践是ε/公差比较-或在可能的情况下使用不太浮动的类型;)是的,这确实是个问题。问题是关于比较二进制浮点值。阅读标题点!Goldberg的论文以html格式免费提供。只需搜索标题。这是最热门的。我更喜欢PDF,因为它更容易阅读,打印效果更好。