C#为简单的数学运算获得奇怪的结果

时间:2013-10-26 12:57:45

标签: c# math console-application

我注意到在添加两个双打时出现了一些奇怪的行为,有时候它是正确的,有时候也没有!

这是第一个例子:

double num1 = 0.1 + 0.7; //Guess the result?

轻松 - 0.8 !!!不是吗?

看看奇怪的结果:

enter image description here

猜猜是什么,if语句在else块内,并打印num1 - 不,它不打印0.799999999999993,它打印0.8。

所以我向前迈出了一步,尝试了这段代码:

if (0.1 + 0.7 == 0.8) //Returns false ??
{
    Console.WriteLine("Correct");
}
好的,很奇怪,但现在我找到了正确的方法,它应该使用f(float)。我记得double有很多空格,所以它可以包含更多的数字,也许这就是原因。

float num2 = 0.1f + 0.7f;

if (num2 == 0.8f) //Perfect - finally works !!!
{
    Console.WriteLine("Correct");
}
else
{
    Console.WriteLine(num2);
}

但是现在,我试试这个 - 再次它返回false,为什么?

if (0.1f + 0.7f == 0.8f) //Returns false :(
{
    Console.WriteLine("Correct");
}

调试时会生成手表:

enter image description here

有人可以解释我这里有什么问题吗?那些错误吗?

提前致谢。

4 个答案:

答案 0 :(得分:6)

浮点运算不精确。您应该阅读文章What Every Computer Scientist Should Know About Floating-Point Arithmetic。如果您希望计算在十进制意义上是精确的,则应使用decimal类型,其中0.1m + 0.7m == 0.8mtrue。您应该注意decimal使用浮点数,就像floatdouble一样,除了基数是10而不是2.因为处理基数2是如此容易和高度优化,decimal要慢得多。它也有自己的不准确性,只有在处理可用短数字(如0.70.1)表示的数字时才准确,这对财务数据有利。

另一篇了解浮点数的有用文章是Wikipedia's Double-precision floating-point format

答案 1 :(得分:1)

在内部,double存储在base-2中的位中。 所有整数都可以用base-2完全表示,但是当它涉及分数时,它就是另一个问题。

与我们在十进制系统中无法精确地表示1/3结果的方式相同,在二元系统中,您无法表示所有分数。由于十进制是基数为10,因此很容易以十进制表示x / 10,但在基数2中,某些x是不可能的。

因此,无论何时使用双打,都要注意一些舍入错误。

出于这个原因,只要需要确切的数字,就不会使用双打,就像货币金额一样。

答案 2 :(得分:1)

调用Math.Round方法以确保两个值具有相同的精度。以下示例修改前一个示例以使用此方法,以便两个小数值相等:

using System;

public class Example
{
   public static void Main()
   {
      double value1 = .333333333333333;
      double value2 = 1.0/3;
      int precision = 7;
      value1 = Math.Round(value1, precision);
      value2 = Math.Round(value2, precision);
      Console.WriteLine("{0:R} = {1:R}: {2}", value1, value2, value1.Equals(value2));
   }
}
// The example displays the following output: 
//        0.3333333 = 0.3333333: True

答案 3 :(得分:1)

以下是答案:http://www.codeproject.com/Articles/383871/Demystify-Csharp-floating-point-equality-and-relat

浮动值仅为近似值。要比较两个浮点值,您需要检查它们之间的差异。差异不能大于float.Epsilon

使用它来比较这些值:

bool AlmostEqualDoubles(double nVal1, double nVal2, int nPrecision = 6)
{
   nPrecision = Math.Max(Math.Min(16, nPrecision), 0);
   double nEpsilon = 1.0;
   for (int i = 0; i < nPrecision; i++) nEpsilon *= 0.1;       
   return (nVal2 - nEpsilon) < nVal1 && nVal1 < (nVal2 + nEpsilon);
}