【问题标题】:Floating point arithmetic is too reliable浮点运算太可靠了
【发布时间】:2010-04-15 17:13:27
【问题描述】:

我了解在现代计算机系统中执行的浮点运算并不总是与实际运算一致。我正在尝试设计一个小型 C# 程序来演示这一点。例如:

static void Main(string[] args)
    {
        double x = 0, y = 0;

        x += 20013.8;
        x += 20012.7;

        y += 10016.4;
        y += 30010.1;

        Console.WriteLine("Result: "+ x + " " + y + " " + (x==y));
        Console.Write("Press any key to continue . . . "); Console.ReadKey(true);
    }

但是,在这种情况下,xy 最终是相等的。

我是否可以使用类似复杂性的程序来证明浮点运算的不一致性,而不使用任何真正疯狂的数字?如果可能的话,我想避免数学上正确的值超出小数点几位。

【问题讨论】:

  • 3/3 != (1/3)*3?我不确定这是否是您正在寻找的,以及它是否真的产生了预期的1 != 0.999999...

标签: c# floating-point double-precision


【解决方案1】:
double x = (0.1 * 3) / 3;
Console.WriteLine("x: {0}", x); // prints "x: 0.1"
Console.WriteLine("x == 0.1: {0}", x == 0.1); // prints "x == 0.1: False"

备注:基于此,不要假设浮点运算在 .NET 中不可靠。

【讨论】:

    【解决方案2】:

    这是一个基于先前问题的示例,该示例演示浮点算术与您想象的不完全一样。

    float f = (13.45f * 20);
    int x = (int)f;
    int y = (int)(13.45f * 20);
    Console.WriteLine(x == y);
    

    在这种情况下,false 被打印到屏幕上。为什么?因为执行数学运算的位置与发生转换为 int 的位置。对于 x,数学运算在一个语句中执行并存储到 f,然后它被强制转换为整数。对于 y,计算的值永远不会在转换之前存储。 (在 x 中,在计算和强制转换之间会丢失一些精度,而不是 y。

    有关浮点数学中具体发生的事情背后的解释,请参阅此问题/答案。 Why differs floating-point precision in C# when separated by parantheses and when separated by statements?

    【讨论】:

    • float f1 = 1.0; float f2 = 10.0; double d = f1/f2; 怎么样。尽管浮点到双精度的转换是隐式执行的,没有警告或类型转换,并且双精度到浮点精度需要显式类型转换,但前者通常会产生错误的结果,而后者很少会。
    【解决方案3】:

    我最喜欢的演示归结为

    double d = 0.1;
    d += 0.2;
    d -= 0.3;
    
    Console.WriteLine(d);
    

    输出不是0

    【讨论】:

      【解决方案4】:

      试着让它小数点不是 0.5。

      在这里看看这篇文章

      http://floating-point-gui.de/

      【讨论】:

      • 这看起来很有希望,但是当我做到 0.4 时它们仍然是平等的
      【解决方案5】:

      尝试将 VERY 大和 VERY 小数相加。小的将被消耗,结果将与大数相同。

      【讨论】:

        【解决方案6】:

        尝试对无理数(例如平方根)或长度很长的重复分数执行重复运算。您会很快看到错误累积。例如,计算 1000000*Sqrt(2) 与 Sqrt(2)+Sqrt(2)+...+Sqrt(2)。

        【讨论】:

          【解决方案7】:

          我现在能想到的最简单的是:

          class Test
          {
              private static void Main()
              {
                  double x = 0.0;
          
                  for (int i = 0; i < 10; ++i)
                      x += 0.1;
          
                  Console.WriteLine("x = {0}, expected x = {1}, x == 1.0 is {2}", x, 1.0, x == 1.0);
                  Console.WriteLine("Allowing for a small error: x == 1.0 is {0}", Math.Abs(x - 1.0) < 0.001);
              }
          }
          

          【讨论】:

            【解决方案8】:

            我建议,如果你真的感兴趣,你可以看看任何一个讨论浮点数的页面,其中一些非常详细。您很快就会意识到,在计算机中,它们是一种妥协,以精度换取范围。如果您要编写使用它们的程序,您确实需要了解它们的局限性和如果您不注意可能出现的问题。值得您花时间。

            【讨论】:

              【解决方案9】:

              double 精确到 ~15 位。您需要更高的精度才能真正开始只用几个浮点运算来解决问题。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2016-08-23
                • 2014-01-24
                • 2012-03-19
                • 2018-01-28
                • 2013-06-12
                • 2014-12-05
                • 2011-07-17
                相关资源
                最近更新 更多