将十进制转换为double时的奇怪行为

时间:2013-08-21 15:03:57

标签: c# .net double decimal rounding-error

将十进制转换为double时,我遇到了一个奇怪的问题。

以下代码返回true:

Math.Round(0.010000000312312m, 2) == 0.01m //true

但是,当我将其转换为double时,它返回false:

(double)Math.Round(0.010000000312312m, 2) == (double)0.01m //false

当我想使用Math.Pow时,我遇到了这个问题,因为十进制没有Math.Pow重载,所以我被迫将十进制转换为double。

这是记录在案的行为吗?当我被迫将十进制转换成双倍时,我怎么能避免它?

Visual Studio截图:

Screenshot from Visual Studio

将Math.Round转换为以下结果加倍:

(double)Math.Round(0.010000000312312m, 2)   0.0099999997764825821   double
(double)0.01m   0.01    double

更新

好的,我正在重现这个问题如下:

  1. 当我运行WPF应用程序并在启动后立即检查输出时,我就像空项目一样真实。
  2. 应用程序的一部分将滑块中的值发送到计算算法。我得到了错误的结果,我在计算方法上加了断点。现在,当我在监视窗口中检查值时,我得到错误(没有任何修改,我只是刷新监视窗口)。
  3. 我在一个较小的项目中重现问题后,我会在这里发布。
  4. UPDATE2

    不幸的是,我无法在较小的项目中重现这个问题。我认为Eric的答案解释了原因。

2 个答案:

答案 0 :(得分:11)

人们在这里的评论中报道,有时比较的结果是真的,有时它是假的。

不幸的是,这是可以预期的。 C#编译器,抖动和CPU都允许在 more 中执行双精度算术,而不是64位双精度,,因为他们认为合适。这意味着有时看起来像“相同”计算的结果可以在一次计算中以64位精度完成,在另一次计算中以80或128位精度完成,并且这两个结果的最后一位可能不同。

让我确保你理解我所说的“他们认为合适”。您可以以任何理由获得不同的结果。您可以在调试和零售中获得不同的结果。如果使编译器在常量中进行计算,并且如果使运行时在运行时进行计算,则可以得到不同的结果。调试器运行时,您可以获得不同的结果。您可以在运行时和调试器的表达式求值程序中获得不同的结果。 任何理由。双重算术固有地不可靠。这是由于浮点芯片的设计;在没有相当大的性能损失的情况下,这些芯片上的双重算术不能更加可重复。

由于这个原因和其他原因你几乎绝不会比较两个完全相等的双打。相反,减去双精度数,并查看差值的绝对值是否小于合理界限。

此外,您必须了解为什么将 double 舍入到两个小数位是一件困难的事情。非零有限双数是形式(1 + f)x 2 e 的数字,其中f是分母,其为2的幂,e是指数。很明显,不可能以这种形式表示0.01,因为没有办法让分母等于等于2的幂的分母中的10的幂。

双精度0.01实际上是二进制数1.0100011110101110000101000111101011100001010001111011 x 2 -7 ,十进制数为0.01000000000000000020816681711721685132943093776702880859375。那就是最接近的你可以在双倍中获得0.01。如果您需要完全那个值,那么使用小数。这就是为什么它被称为十进制

顺便说一句,我已经在StackOverflow上多次回答了这个问题。例如:

Why differs floating-point precision in C# when separated by parantheses and when separated by statements?

另外,如果你需要“拆开”一个双重来看它的位是什么,这个我在一段时间后掀起的方便代码是非常有用的。它要求您安装Solver Foundation,但这是免费下载。

http://ericlippert.com/2011/02/17/looking-inside-a-double/

答案 1 :(得分:0)

这是记录在案的行为。十进制数据类型比double类型更精确。因此,当您从十进制转换为双精度时,可能会丢失数据。这就是您需要对类型进行显式转换的原因。

有关更多信息,请参阅以下MSDN C#参考:

十进制数据类型:http://msdn.microsoft.com/en-us/library/364x0z75(v=vs.110).aspx

双数据类型:http://msdn.microsoft.com/en-us/library/678hzkk9(v=vs.110).aspx

投射和类型转换:http://msdn.microsoft.com/en-us/library/ms173105.aspx