【问题标题】:Why comparing double and float leads to unexpected result? [duplicate]为什么比较 double 和 float 会导致意外结果? [复制]
【发布时间】:2011-10-07 00:41:42
【问题描述】:

可能重复:
strange output in comparision of float with float literal

float f = 1.1;
double d = 1.1;
if(f == d) // returns false!

为什么会这样?

【问题讨论】:

  • 谷歌“每个计算机科学家都应该知道的关于浮点运算的知识”以获得一些有趣的阅读。
  • 永远不要使用== 来比较浮点值。而是使用更多类似if (abs(f - d) < 0.001) 的东西。
  • @Bart:它实际上在 C++ 标记部分 => stackoverflow.com/tags/c%2b%2b/info:只需向下滚动。您将看到标签What Every Computer Scientist Should Know About Floating-Point Arithmetic
  • @aroth 坏主意:如果 f 是 0.0001 而 d 是 0.00001 怎么办?
  • @stijn - 这只是一个例子。显然,如果您使用非常小的数字(或其他任何需要非常高精度的数据),那么您可能想要使用不同的方法。比如根本不使用doublefloat,而是找到一个好的任意精度算术库。

标签: c++ visual-c++


【解决方案1】:

IEEE 754 32-bit float 可以存储:1.1000000238...
IEEE 754 64位double可以存储:1.1000000000000000888...

看看为什么它们不“相等”?


在 IEEE 754 中,分数以 2 的幂的形式存储:

2^(-1), 2^(-2), 2^(-3), ...
1/2,    1/4,    1/8,    ...

现在我们需要一种表示0.1 的方法。这是 32 位 IEEE 754 表示(浮点数)的(简化版本):

2^(-4) + 2^(-5) + 2^(-8) + 2^(-9) + 2^(-12) + 2^(-13) + ... + 2^(-24) + 2^(-25) + 2^(-27)
00011001100110011001101
1.10000002384185791015625

使用 64 位 double,它更加准确。它不会在2^(-25) 停止,它会持续大约两倍。 (2^(-48) + 2^(-49) + 2^(-51),也许吧?)


资源

IEEE 754 Converter(32 位)

【讨论】:

    【解决方案2】:

    浮点数和双精度数以二进制格式存储,不能准确表示每个数字(不可能在有限空间中表示无限多可能的不同数字)。

    因此他们会进行四舍五入。 Float 必须舍入超过 double,因为它更小,所以 1.1 舍入到最接近的有效 Float 与 1.1 舍入到最接近的 Double 值不同。

    要查看哪些数字是有效的浮点数和双精度数,请参阅 Floating Point

    【讨论】:

      【解决方案3】:

      floatdouble 数字考虑的重要因素是:
      精度 & 舍入


      精度:
      浮点数的精度是它可以表示多少位而不丢失它所包含的任何信息。

      考虑分数1/3。这个数字的十进制表示是0.33333333333333…,其中 3 一直到无穷大。无限长的数字需要以精确的精度描述无限的内存,但floatdouble 数据类型通常只有48 字节。因此浮点数和双精度数只能存储一定数量的数字,其余的必然会丢失。因此,对于浮点数或双精度数,没有明确的准确方法来表示需要比变量所能容纳的精度更高的数字。


      四舍五入:
      binarydecimal (base 10) 数字之间存在不明显的区别。
      考虑分数1/10。在decimal 中,这可以很容易地表示为0.1,而0.1 可以被认为是一个容易表示的数字。但是,在二进制中,0.1 由无限序列表示:0.00011001100110011…

      一个例子:

      #include <iomanip>
      int main()
      {
          using namespace std;
          cout << setprecision(17);
          double dValue = 0.1;
          cout << dValue << endl;
      }
      

      这个输出是:

      0.10000000000000001
      

      不是

      0.1.
      

      这是因为由于内存有限,double 必须截断近似值,这导致数字不完全是 0.1。这种情况称为舍入误差


      当比较两个接近的浮点数和双精度数时,会出现这种舍入错误,最终比较会产生不正确的结果,这就是您永远不应该使用== 比较浮点数或双精度数的原因。

      你能做的最好的就是取它们的差值并检查它是否小于一个 epsilon。

      abs(x - y) < epsilon
      

      【讨论】:

      • 这是一个有效的答案,+1 可以抵消反对票。它留下了需要的信息,但仍然有效。
      • 它们可以相当准确地表示,但并非在所有情况下都绝对准确。
      • @Als:这个信息要好得多。将我的反对票转换为赞成票,并删除了我的反对意见 :)
      【解决方案4】:

      通常,您不应使用 == 比较浮点数与浮点数、双精度数与双精度数或浮点数与双精度数。

      最好的做法是减去它们,并检查差的绝对值是否小于一个小的 epsilon。

      if(std::fabs(f - d) < std::numeric_limits<float>::epsilon())
      {
          // ...
      }
      

      一个原因是因为浮点数(或多或少)是二进制分数,并且只能近似许多十进制数。许多十进制数必须转换为重复的二进制“十进制”或无理数。这将引入舍入误差。

      From wikipedia:

      例如,1/5 不能使用二进制基数精确表示为浮点数,但可以使用十进制基数精确表示。

      在您的特定情况下,浮点数和双精度数对于必须用于以二进制表示 1.1 的无理/重复分数将具有不同的舍入。在它们的相应转换引入不同级别的舍入误差之后,您将很难让它们“相等”。

      我上面给出的代码通过简单地检查值是否在一个非常短的增量内来解决这个问题。您的比较从“这些值是否相等?”发生了变化。到“这些值之间的误差是否在很小的范围内?”

      另外,请参阅此问题:What is the most effective way for float and double comparison?

      关于浮点数还有许多其他奇怪的东西会破坏简单的相等比较。查看这篇文章以获取其中一些的描述:

      http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm

      【讨论】:

      • ε-neighbourhood 通常不是最佳实践,而是一种懒惰的解决方法。大多数情况下,您实际上并不真正想要==,而是&lt;=&gt;=,并且只是认为您需要==,因为这恰好适用于整数,因为这些情况永远不会达到&gt;。 — 此外,有时您确实实际上希望 == 用于浮动,尽管这种情况很少发生。
      • @leftaroundabout:这与我读过的大多数文章相反。你能详细说明这些案例吗?也许添加您自己的答案:)
      【解决方案5】:

      尝试运行这段代码,结果将原因显而易见。

      #include <iomanip>
      #include <iostream>
      
      int main()
      {
        std::cout << std::setprecision(100) << (double)1.1 << std::endl;
        std::cout << std::setprecision(100) << (float)1.1 << std::endl;
        std::cout << std::setprecision(100) << (double)((float)1.1) << std::endl;
      }
      

      输出:

      1.100000000000000088817841970012523233890533447265625
      1.10000002384185791015625
      1.10000002384185791015625
      

      floatdouble 都不能准确地表示 1.1。当您尝试进行比较时,浮点数会隐式上转换为双精度数。 double数据类型可以准确表示float的内容,所以比较结果为false。

      【讨论】:

        猜你喜欢
        • 2016-11-15
        • 2016-08-20
        • 2010-10-26
        • 1970-01-01
        • 2018-11-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多