【问题标题】:C# Getting strange results for a simple math operationsC# 得到一个简单数学运算的奇怪结果
【发布时间】:2013-10-26 12:57:45
【问题描述】:

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

这是第一个例子:

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

简单 - 0.8 !!!还是不行?

看看奇怪的结果:

你猜怎么着,if 语句进入 else 块,并打印 num1 - 不,它不打印 0.7999999999999993,它打印 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");
}

调试时的watch结果:

有人可以解释一下这里出了什么问题吗?是bug吗?

提前致谢。

【问题讨论】:

  • @MichaC 提问的方式完全不同,也不一样。
  • 当然,你在这个问题上付出了更多的努力,但最后,还是一样的。只是因为 C# 中的浮点运算并不精确。已经在这个网站上回答了 1000 次 ^^

标签: c# math console-application


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

    在内部,double 以位存储,以 base-2 为单位。 所有整数都可以精确地以 2 为底数表示,但如果是分数,那就另当别论了。

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

    因此,无论何时使用双精度数,请始终注意一些舍入错误。

    因此,当需要精确数字时,不要使用双精度数,例如货币金额。

    【讨论】:

    • 在第二段中,您的意思肯定是“对于某些x 来说是不可能的”。例如,5/10 = 1/2 可以精确地表示为以 2 为底的浮点数。
    • 但这句话是对的:二进制中的 0.1 与十进制中的 1/3 具有相同的问题。
    【解决方案3】:

    调用 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
    

    【讨论】:

      【解决方案4】:

      这里是答案: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);
      }
      

      【讨论】:

      • “浮点值只是近似值”并非完全正确。说明这一点的更好方法是许多十进制值不能精确地映射到 IEEE 浮点数。但是,可以使用 2 的幂分解的值在映射到浮点数(例如 4、2、1、.5、.25、.125 等)时将具有精确值。同样的问题出现在十进制世界中,例如 1/3 等。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-08-22
      • 1970-01-01
      相关资源
      最近更新 更多