为什么比较double和float导致出乎意料的结果?

时间:2011-07-17 06:28:00

标签: c++ visual-c++

  

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

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

为什么会这样?

5 个答案:

答案 0 :(得分:32)

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


<强>精密:
浮点数的精度是它可以表示的位数,而不会丢失它包含的任何信息。

考虑分数1/3。此数字的十进制表示为0.33333333333333…,其中3表示无穷大。无限长度数将要求以精确的精度描绘无限内存,但floatdouble数据类型通常只有48个字节。因此浮点&amp;双数字只能存储一定数量的数字,其余数字必然会丢失。因此,没有明确准确的方法来表示浮点数或双数字,其数字需要比变量可以保持的精度更高。


<强>四舍五入:
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.

这是因为双重必须截断近似值,因为它的内存有限,导致数字不完全是0.1。这种情况称为舍入错误


每当比较两个关闭的浮点数和双数字时,这样的舍入错误就会启动并最终比较产生不正确的结果,这就是你不应该使用==来比较浮点数或加倍的原因。

你能做的最好的就是区分它们并检查它是否小于epsilon。

abs(x - y) < epsilon

答案 1 :(得分:8)

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

#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。 double数据类型可以准确地表示float的内容,因此比较产生false。

答案 2 :(得分:4)

通常情况下,你不应该使用==将浮点数与浮点数进行比较,双精度数转换为双精度数,或浮点数加倍数。

最佳做法是减去它们,并检查差异的绝对值是否小于小epsil。

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

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

From wikipedia

  

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

在您的特定情况下,float和double将对无理/重复分数进行不同的舍入,必须使用它来表示二进制中的1.1。在相应的转换引入不同级别的舍入错误后,您将很难让它们“平等”。

我上面给出的代码通过简单地检查值是否在非常短的delta内来解决这个问题。您的比较是否会改变“这些值是否相等?” “这些值是否在彼此之间的误差范围内?”

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

还有很多关于浮点数的奇怪之处,它们打破了简单的相等比较。查看本文以获取其中一些内容的说明:

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

答案 3 :(得分:2)

IEEE 754 32位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位)

答案 4 :(得分:0)

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

结果他们做了四舍五入。 Float必须舍入两倍以上,因为它更小,因此1.1舍入到最近的有效Float不同于1.1舍入到最近的valud Double。

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