为什么比较double和float会导致意想不到的结果?

可能重复:
浮点数与浮点数相比,奇怪的输出

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

为什么这样?

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


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

考虑分数1/3 。 这个数字的十进制表示是0.33333333333333… 3的无限远。 一个无限长度的数字将需要以精确的精度来描述无限的内存,但是float或者double数据types通常只有4或者8个字节。 因此,浮点数和双数只能存储一定数量的数字,其余的数字一定会丢失。 因此,没有确切的方法来表示浮点数或双精度数的数字,这些精度要比variables能保持的精度要高。


四舍五入:
binarydecimal (base 10)数字之间有一个非明显的差异。
考虑1/10分数。 在decimal ,这可以很容易地表示为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 。 这种情况被称为舍入错误


每当比较两个closures的浮点数和双精度数时,这样的舍入误差就会引入,最终导致比较结果不正确,这就是您不应该使用==比较浮点数或双精度浮点数的原因。

你可以做的最好的是采取他们的差异,并检查它是否小于一个epsilon。

 abs(x - y) < epsilon 

尝试运行这个代码,结果会使原因显而易见。

 #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 

无论是float还是double都不能准确表示1.1。 当您尝试进行比较时,浮点数被隐式上转换为double。 double数据types可以准确地表示float的内容,所以比较结果是错误的。

一般来说,你不应该比较漂浮物浮动,双打双打或漂浮使用==双打。

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

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

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

从维基百科 :

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

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

我上面给出的代码通过简单地检查这些值是否在很短的三angular洲内解决了这个问题。 你的比较从“这些值是否相等? 到“这些值是否在相互之间的一个小误差范围之内?

另外,看到这个问题: 什么是最有效的方式进行浮点和双重比较?

还有很多关于浮点数的其他奇怪的事情,这些奇怪的事情都会打破简单的平等比较。 检查这篇文章的一些他们的描述:

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

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转换器 (32位)

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

结果他们四舍五入。 浮点数要翻一倍以上,因为它比较小,所以1.1舍入到最近的有效浮点数不同于1.1舍入到最近的valud Double。

要查看哪些数字是有效的浮点数和双精度浮点数