为什么比较double和float会导致意想不到的结果?
可能重复:
浮点数与浮点数相比,奇怪的输出
float f = 1.1; double d = 1.1; if(f == d) // returns false!
为什么这样?
float
或double
数考虑的重要因素是:
精确度和舍入
精确:
浮点数的精度是它可以表示多less位数,而不会丢失它所包含的任何信息。
考虑分数1/3
。 这个数字的十进制表示是0.33333333333333…
3的无限远。 一个无限长度的数字将需要以精确的精度来描述无限的内存,但是float
或者double
数据types通常只有4
或者8
个字节。 因此,浮点数和双数只能存储一定数量的数字,其余的数字一定会丢失。 因此,没有确切的方法来表示浮点数或双精度数的数字,这些精度要比variables能保持的精度要高。
四舍五入:
binary
和decimal (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。
要查看哪些数字是有效的浮点数和双精度浮点数