【问题标题】:What is a simple example of floating point/rounding error?什么是浮点/舍入误差的简单示例?
【发布时间】:2010-09-19 22:51:30
【问题描述】:

我听说过使用浮点变量时出现“错误”。现在我正在尝试解决这个难题,我想我遇到了一些舍入/浮点错误。所以我终于要弄清楚浮点错误的基本原理了。

什么是浮点/舍入错误的简单示例(最好在 C++ 中)?

编辑:例如,假设我有一个成功概率为 p 的事件。我做了 10 次这个事件(p 不变,所有试验都是独立的)。恰好 2 次成功试验的概率是多少?我将其编码为:

double p_2x_success = pow(1-p, (double)8) * pow(p, (double)2) * (double)choose(8, 2);

这是浮点错误的机会吗?

【问题讨论】:

标签: c++ floating-accuracy


【解决方案1】:

一般来说,浮点错误是指无法以 IEEE 浮点表示形式存储的数字。

整数存储时最右边的位为 1,左边的每一位都是 (2,4,8,...) 的两倍。不难看出,它可以存储最大为 2^n 的任意整数,其中 n 是位数。

浮点数的尾数(小数部分)以类似的方式存储,但从左到右移动,并且每个连续的位是前一个值的一半。 (实际上比这要复杂一点,但现在就可以了)。

因此,像 0.5 (1/2) 这样的数字很容易存储,但并不是每个

一个非常简单的例子是 0.1 或 1/10。这可以通过无限系列来完成(我真的不介意计算),但是每当计算机存储 0.1 时,存储的并不完全是这个数字。

如果您可以使用 Unix 机器,很容易看到这一点:

Python 2.5.1 (r251:54863, Apr 15 2008, 22:57:26) 
[GCC 4.0.1 (Apple Inc. build 5465)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> 0.1
0.10000000000000001
>>> 

无论您使用哪种语言,您都需要非常小心使用浮点数和双精度数进行相等性测试。

(对于您的示例,0.2 是另一个无法存储在 IEEE 二进制中的讨厌数字,但只要您测试不等式,而不是等式,例如 p

【讨论】:

    【解决方案2】:

    一个简单的 C 语言示例让我很感兴趣:

    double d = 0;
    sscanf("90.1000", "%lf", &d);
    printf("%0.4f", d);
    

    这打印90.0999

    这是一个将 DMS 中的角度转换为弧度的函数。

    为什么在上述情况下不起作用?

    【讨论】:

    • 正如一位匿名用户指出的那样,sscanf 的“f”转换说明符需要float 参数,而不是double(但是,“f”表示double 到@987654327 @ - 是的,这很令人困惑)。应该使用“lf”修改的转换说明符使sscanfdouble 一起工作。
    【解决方案3】:
     for(double d = 0; d != 0.3; d += 0.1); // never terminates 
    

    【讨论】:

      【解决方案4】:

      图片值一千字 - 尝试画方程f(k) :

      你会得到这样的XY图(X和Y是对数刻度)。

      如果计算机可以表示 32 位浮点数而没有舍入误差,那么对于每个 k,我们应该得到零。但由于浮点误差累积,误差随着 k 值的增大而增加。

      hth!

      【讨论】:

      • 我可以在 CC0 许可下将此图像(重做,因此它是 SVG)添加到 Wikipedia Commons(参考这个想法)吗?
      【解决方案5】:

      这是一个吸引我的:

       round(256.49999) == 256
      roundf(256.49999) == 257
      

      双精度和浮点数具有不同的精度,因此第一个将表示为256.49999000000003,第二个将表示为256.5,因此将进行不同的舍入

      【讨论】:

        【解决方案6】:

        我喜欢 Python 解释器中的这个:

        Python 2.7.10 (default, Oct  6 2017, 22:29:07) 
        [GCC 4.2.1 Compatible Apple LLVM 9.0.0 (clang-900.0.31)] on darwin
        Type "help", "copyright", "credits" or "license" for more information.
        >>> 0.1+0.2
        0.30000000000000004
        >>>
        

        【讨论】:

          【解决方案7】:

          超级简单:

          a = 10000000.1
          b = 1/10
          print(a - b == 10000000)
          print ('a:{0:.20f}\nb:{1:.20f}'.format(a, b))
          

          打印(取决于平台)类似:

          False                                                                                                                                 
          a:10000000.09999999962747097015                                                                                                       
          b:0.10000000000000000555 
          

          【讨论】:

          • 这是什么语言?问题被标记为 C++,这段代码根本没有意义(1/10 完全可以用浮点表示,它是0.00000
          【解决方案8】:

          这是我想到的最简单的方法,应该适用于多种语言:

          0.2 + 0.1
          

          这里有一些我想到的 REPL 示例,但应该在任何符合 IEEE754 的语言上返回此结果。

          Python

          >>> 0.2 + 0.1
          0.30000000000000004
          

          科特林

          0.2 + 0.1
          res0: kotlin.Double = 0.30000000000000004
          

          斯卡拉

          scala> 0.2 + 0.1
          val res0: Double = 0.30000000000000004
          

          Java

          jshell> 0.2 + 0.1
          $1 ==> 0.30000000000000004
          

          红宝石

          irb(main):001:0> 0.2 + 0.1
          => 0.30000000000000004
          

          【讨论】:

          • 其实Lua和PHP都返回0.3
          • 还有 Perl :)
          【解决方案9】:

          我认为 Ruby 在its documentation 中有一个很好的例子:

          sum = 0
          10_000.times do
            sum = sum + 0.0001
          end
          print sum #=> 0.9999999999999062
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2018-09-01
            • 1970-01-01
            • 1970-01-01
            • 2020-08-04
            • 1970-01-01
            • 1970-01-01
            • 2019-01-21
            相关资源
            最近更新 更多