【问题标题】:Comparing double values in C#比较 C# 中的双精度值
【发布时间】:2009-09-09 10:14:25
【问题描述】:

我有一个名为xdouble 变量。 在代码中,x 被赋值为0.1,我在比较x0.1 的“if”语句中检查它

if (x==0.1)
{
----
}

很遗憾它没有进入if语句

  1. 我应该使用Double还是double

  2. 这背后的原因是什么?您能为此提出解决方案吗?

【问题讨论】:

标签: c# .net double


【解决方案1】:

由于计算机如何存储浮点值,这是一个标准问题。在此处搜索“浮点问题”,您会发现大量信息。

简而言之——float/double 不能精确存储0.1。总会有一点偏差。

您可以尝试使用decimal 类型,它以十进制表示法存储数字。因此0.1 将可以精确表示。


你想知道原因:

Float/double 存储为二进制分数,而不是十进制分数。举例说明:

12.34 在十进制表示法中(我们使用的)是什么意思

1 * 101 + 2 * 100 + 3 * 10-1 + 4 * 10-2支持>

计算机以相同的方式存储浮点数,只是它使用基数210.01 表示

1 * 21 + 0 * 20 + 0 * 2-1 + 1 * 2-2支持>

现在,您可能知道有些数字无法用我们的十进制表示法完全表示。例如,十进制的1/30.3333333…。同样的事情发生在二进制符号中,除了不能精确表示的数字是不同的。其中有号码1/10。二进制表示,即0.000110011001100…

由于二进制不能精确存储,所以采用四舍五入的方式存储。因此你的问题。

【讨论】:

  • 我完全理解你上面说的。但是,为什么,如果他写x = 0.01; 来分配x,然后与文字0.01 进行比较,x0.01 的实际底层二进制值不会相同(再次,假设没有计算用于分配x)。因此,(x == 0.01) 应该可以工作,不是吗?但是,一旦您使用 x 执行一些算术运算,所有的赌注都将被取消。另外,我会更明确:if (x == 0.01d) 明确声明0.01 是双精度(而不是将0.01float 转换为double)——这可能是问题所在。跨度>
  • @fourpastmidnight:我无法重现您所说的内容(.NET 4)。如果我将0.010.01 进行比较,我会得到true
  • 是的,如果你这样做x = 0.01; if ( x == 0.01),那么比较应该是正确的。好吧,假设没有任何隐式的浮点到双精度数据类型转换(尽管可能在那时)。但 OP 没有具体说明他是如何获得第一个 0.01 的。它很可能确实来自计算。
  • @Vilx - 确实,OP 没有说明如何分配 x 值。如果该值是通过迭代计算分配的(意思是最终结果约为 0.01),那么这可能是“预期的”。 :)
  • 对我来说,它返回 true?为什么?
【解决方案2】:

doubleDouble 相同(doubleDouble 的别名),可以互换使用。

将双精度值与另一个值进行比较的问题在于,双精度值是近似值,而不是精确值。因此,当您将x 设置为0.1 时,它实际上可能存储为0.100000001 或类似的东西。

您应该检查差异是否小于定义的最小差异(容差),而不是检查是否相等。类似的东西:

if (Math.Abs(x - 0.1) < 0.0000001)
{
    ...
}

【讨论】:

    【解决方案3】:

    您需要在X-Y 上组合Math.Absvalue 进行比较。

    您可以使用以下扩展方法方法

    public static class DoubleExtensions
        {
            const double _3 = 0.001;
            const double _4 = 0.0001;
            const double _5 = 0.00001;
            const double _6 = 0.000001;
            const double _7 = 0.0000001;
    
            public static bool Equals3DigitPrecision(this double left, double right)
            {
                return Math.Abs(left - right) < _3;
            }
    
            public static bool Equals4DigitPrecision(this double left, double right)
            {
                return Math.Abs(left - right) < _4;
            }
    
            ...
    

    由于除了ToString 之外,您很少在 double 上调用方法,我相信它的扩展非常安全。

    那你可以比较xylike

    if(x.Equals4DigitPrecision(y))

    【讨论】:

    • 它可能是 Double struct 的不错扩展!
    【解决方案4】:

    由于舍入,不能总是精确地比较浮点数。比较

    (x == .1)
    

    电脑真的会比较

    (x - .1) vs 0
    

    由于浮点数在机器上的表示方式,不能总是精确地表示提取的结果。因此,您会得到一些非零值,并且条件评估为 false

    为了克服这种比较

    Math.Abs(x- .1) vs some very small threshold ( like 1E-9)
    

    【讨论】:

    • 你能举例说明清楚吗???有什么解决方案让我需要更改我的声明??
    【解决方案5】:

    来自documentation

    比较精确 应谨慎使用 Equals 方法,因为由于两个值的精度不同,两个明显等效的值可能不相等。以下示例报告 Double 值 .3333 和 1 除以 3 返回的 Double 不相等。

    ...

    一种推荐的技术不是比较相等性,而是定义两个值之间可接受的差异范围(例如其中一个值的 0.01%)。如果两个值之间的差值的绝对值小于或等于该余量,则差异可能是由于精度差异造成的,因此这些值很可能相等。下面的示例使用这种技术来比较 .33333 和 1/3,这是前面的代码示例发现不相等的两个 Double 值。

    所以如果你真的需要一个替身,你应该使用文档中描述的技术。 如果可以,请将其更改为小数。 会慢一些,但你不会遇到这种问题。

    【讨论】:

      【解决方案6】:

      使用@987654321@。它没有这个“问题”。

      【讨论】:

      • 十进制值的操作要慢得多。我们不应该总是仅仅因为它“没有这个问题”就使用十进制类型。两种类型都有自己的用途。
      【解决方案7】:

      众所周知,由于舍入和内部表示问题,浮点值的精确比较并不总是有效。

      尝试不精确的比较:

      if (x >= 0.099 && x <= 0.101)
      {
      }
      

      另一种选择是使用十进制数据类型。

      【讨论】:

        【解决方案8】:

        double(小写)只是System.Double 的别名,所以它们是相同的。

        原因见Binary floating point and .NET。 简而言之:双精度类型不是精确类型,“x”和“0.1”之间的微小差异会将其丢弃。

        【讨论】:

        【解决方案9】:

        Double(在某些语言中称为 float)由于舍入问题而存在问题,仅当您需要近似值时才适用。

        Decimal 数据类型可以满足您的需求。

        对于引用 decimal 和 Decimal 在 .NET C# 中是相同的,就像 double 和 Double 类型一样,它们都指代相同的类型(尽管如您所见,decimal 和 double 非常不同)。

        请注意,Decimal 数据类型有一些与之相关的成本,因此如果您正在查看循环等,请谨慎使用它。

        【讨论】:

          【解决方案10】:

          官方 MS 帮助,特别是对问题上下文中的“比较中的精确度”部分感兴趣。 https://docs.microsoft.com/en-us/dotnet/api/system.double.equals

          // Initialize two doubles with apparently identical values
          double double1 = .333333;
          double double2 = (double) 1/3;
          // Define the tolerance for variation in their values
          double difference = Math.Abs(double1 * .00001);
          
          // Compare the values
          // The output to the console indicates that the two values are equal
          if (Math.Abs(double1 - double2) <= difference)
             Console.WriteLine("double1 and double2 are equal.");
          else
             Console.WriteLine("double1 and double2 are unequal.");
          

          【讨论】:

            【解决方案11】:

            1) 我应该使用 Double 还是 double???

            Doubledouble 是一回事。 double 只是一个 C# 关键字,用作 System.Double 类的别名 最常见的是使用别名! string (System.String)、int(System.Int32) 也一样

            另见Built-In Types Table (C# Reference)

            【讨论】:

              【解决方案12】:

              从 Java 代码库中获取提示,尝试使用 .CompareTo 并测试零比较。这假设 .CompareTo 函数以准确的方式考虑浮点相等性。例如,

              System.Math.PI.CompareTo(System.Math.PI) == 0
              

              这个谓词应该返回true

              【讨论】:

              • 这是不正确的。 CompareTo 简单使用 ==,容易出现舍入错误
              • @RM :感谢您的评论,防止我犯错。根据MSDN documentation on Double.CompareTo:“值必须相同才能被视为相等。特别是当浮点值依赖于多个数学运算时,它们通常会丢失精度并且它们的值几乎相同,除了它们的最低有效数字. 正因为如此,CompareTo 方法的返回值有时可能看起来令人惊讶。"
              【解决方案13】:
              // number of digits to be compared    
              public int n = 12 
              // n+1 because b/a tends to 1 with n leading digits
              public double MyEpsilon { get; } = Math.Pow(10, -(n+1)); 
              
              public bool IsEqual(double a, double b)
              {
                  // Avoiding division by zero
                  if (Math.Abs(a)<= double.Epsilon || Math.Abs(b) <= double.Epsilon)
                      return Math.Abs(a - b) <=  double.Epsilon;
              
                  // Comparison
                  return Math.Abs(1.0 - a / b) <=  MyEpsilon;
              }
              

              说明

              使用除法 a/b 完成的主要比较功能应该接近 1。但是为什么要除法呢?它只是将一个数字作为参考定义第二个数字。例如

              a = 0.00000012345
              b = 0.00000012346
              a/b = 0.999919002
              b/a = 1.000081004
              (a/b)-1 = 8.099789405475458e-5‬
              1-(b/a) = 8.100445524503848e-5‬
              

              a=12345*10^8
              b=12346*10^8 
              a/b = 0.999919002
              b/a = 1.000081004
              (a/b)-1 = 8.099789405475458e-5‬
              1-(b/a) = 8.100445524503848e-5‬
              

              通过除法,我们消除了影响我们对数字精度判断的尾随或前导零(或相对较小的数字)。在示例中,比较的顺序是 10^-5,我们有 4 个数字精度,因为在开始的代码中我写了与 10^(n+1) 的比较,其中 n 是数字精度。

              【讨论】:

              • Microsoft 建议“在比较 Double 值是否相等时不要使用 Epsilon。”reference
              • Microsoft 提到“通常”ε 非常小,无法用于比较。我没有用它来比较a和b。我用它作为零的指示并避免被零除。 a 和 b 之间的比较是使用我的自定义 Epsilon 完成的(为了清楚起见,我将其更改为 MyEpsilon)。
              【解决方案14】:

              添加到 Valentin Kuzub 的回答 above:

              我们可以使用支持提供第 n 个精度数的单一方法:

              public static bool EqualsNthDigitPrecision(this double value, double compareTo, int precisionPoint) =>
                  Math.Abs(value - compareTo) < Math.Pow(10, -Math.Abs(precisionPoint));
              

              注意:此方法是为了简单而构建的,没有增加体积,也没有考虑到性能。

              【讨论】:

                【解决方案15】:

                作为一般规则:

                双重表示在大多数情况下已经足够好,但在某些情况下可能会失败。如果您需要完整的精度(如在金融应用程序中),请使用十进制值。

                双精度数的大多数问题并非来自直接比较,它曾经是数个数学运算累积的结果,这些运算会因舍入和小数误差(尤其是乘法和除法)而以指数方式干扰值。

                检查你的逻辑,如果代码是:

                x = 0.1
                
                if (x == 0.1)
                

                它不应该失败,失败很简单,如果 X 值是通过更复杂的方式或操作计算的,那么调试器使用的 ToString 方法很可能正在使用智能舍入,也许你可以做同样的事情(如果那是回到使用小数太冒险了):

                if (x.ToString() == "0.1")
                

                【讨论】:

                  【解决方案16】:

                  由于浮点数在内部存储的方式,浮点数表示是出了名的不准确。例如。 x 实际上可能是 0.09999999990.100000001 并且您的条件将失败。如果要确定浮点数是否相等,则需要指定它们是否在一定的容差范围内相等。

                  即:

                  if(Math.Abs(x - 0.1) < tol) {
                     // Do something 
                  }
                  

                  【讨论】:

                  • 并输入一个 Math.Abs​​,以防 x 略小于 0.1。您的代码将接受 x== -10。
                  【解决方案17】:

                  我的双重比较的扩展方法:

                  public static bool IsEqual(this double value1, double value2, int precision = 2)
                  {
                      var dif = Math.Abs(Math.Round(value1, precision) - Math.Round(value2, precision));
                      while (precision > 0)
                      {
                          dif *= 10;
                          precision--;
                      }
                  
                      return dif < 1;
                  }
                  

                  【讨论】:

                    【解决方案18】:

                    比较浮点、双精度或浮点类型,使用CSharp的具体方法:

                    if (double1.CompareTo(double2) > 0)
                    {
                        // double1 is greater than double2
                    }
                    if (double1.CompareTo(double2) < 0)
                    {
                        // double1 is less than double2
                    }
                    if (double1.CompareTo(double2) == 0)
                    {
                        // double1 equals double2
                    }
                    

                    https://docs.microsoft.com/en-us/dotnet/api/system.double.compareto?view=netcore-3.1

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 2015-12-17
                      • 1970-01-01
                      • 1970-01-01
                      • 2018-06-05
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2023-03-11
                      相关资源
                      最近更新 更多