7

我注意到在添加两个双打时出现了一些奇怪的行为,有时它可以正常工作,有时却不行!

这是第一个例子:

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

容易 - 0.8 !!! 或不?

看看奇怪的结果:

在此处输入图像描述

猜猜看,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);
}

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

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

调试时的手表结果:

在此处输入图像描述

有人可以解释一下这里有什么问题吗?是那些虫子吗?

提前致谢。

4

4 回答 4

8

浮点运算并不精确。您应该阅读文章What Every Computer Scientist Should Know About Floating-Point Arithmetic。如果您希望您的计算在十进制意义上是精确的,您应该使用decimal类型,其中0.1m + 0.7m == 0.8mis true。您应该注意,它decimal使用浮点数,就像floatand一样double,除了基数是 10,而不是 2。由于处理基数 2 非常容易且高度优化,decimal因此速度要慢得多。它也有其自身的不准确之处,它仅在处理可以用少量十进制数字(如0.70.1)表示的数字时才准确,这对财务数据很有好处。

另一个了解浮点数的有用文章是维基百科的双精度浮点格式

于 2013-10-26T13:05:52.363 回答
2

在内部,double 以位存储,以 base-2 为单位。所有整数都可以精确地以 2 为基数表示,但是当涉及到分数时,它就另当别论了。

就像我们不能在十进制系统中精确地表示 1/3 的结果一样,在二进制系统中你不能表示所有分数。由于十进制是以 10 为底的,因此用十进制表示 x/10 很容易,但在以 2 为底的情况下,某些 x 是不可能的。

因此,每当您使用双打时,请始终注意一些舍入错误。

出于这个原因,当需要确切的数字时,不要使用双精度数,例如货币金额。

于 2013-10-26T13:08:49.177 回答
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
于 2013-10-26T13:09:21.850 回答
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);
}
于 2013-10-26T13:13:03.310 回答