2011-07-17 40 views
15

可能重复:
strange output in comparision of float with float literal为什么比较double和float会导致意想不到的结果?

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

为什么会这样呢?

+0

http://www.boost.org/doc/libs/1_34_1/libs/test/doc/components/test_tools/floating_point_comparison.html – darlinton

+11

谷歌“每个计算机科学家应该知道的有关浮点运算的内容” 。 – Bart

+2

切勿使用'=='来比较浮点值。相反,使用更多沿着'if(abs(f - d)<0.001)'行的东西。 – aroth

回答

30

考虑的重要因素,与floatdouble号码为:
精密 & 四舍五入


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

考虑分数1/3。这个数字的十进制表示形式是0.33333333333333…,其中3的数字是无限的。无限长度的数字将需要精确描述无限存储器,但floatdouble数据类型通常只有48字节。因此浮点数为&的双数只能存储一定数量的数字,其余的则必然会丢失。因此,没有确切的方法用浮点数或双精度数表示需要比变量可以保持更高精度的数字。


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

这是然而,在二进制,0.1由无限序列表示因为由于它的有限的记忆,双重必须截断近似值y,这会导致一个数字不完全是0.1。这种情况称为舍入错误


当比较两个密切float和double数字,例如舍入误差踢,最终比较产生不正确的结果,这是你永远不应该比较浮点数或使用==双的原因。

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

abs(x - y) < epsilon 
+0

这是一个有效的答案,用于抵消downvotes的+1。 它留下了所需的信息,但都是一样的。 –

+1

他们可以相当准确地表示,只是不是绝对精确地在所有情况下。 – aroth

+1

@Als:这个信息好多了。将我的倒票转换为upvote,并删除了我的反对意见:) –

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位)

0

花车和双打都存储在不能代表每一个数字完全是一个二进制格式(在有限空间中无法表示无限多个可能的不同数字)。

因此他们做舍入。浮点数必须翻一倍以上,因为它比较小,所以1.1舍入到最近的有效浮点数不同于1.1舍入到最接近的valud Double。

要看到什么号码是有效的花车和双打看到Floating Point

4

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

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

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

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

From wikipedia

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

在您的具体情况下,浮点数和double将对用于表示1.1二进制数的无理数/重复小数有不同的舍入。在相应的转换引入不同级别的舍入误差后,您将很难让它们“平等”。

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

而且,看到了这个问题:What is the most effective way for float and double comparison?

也有很多关于那个飘打破了简单的相等比较点数其他古怪的。检查这篇文章的一些人的描述:

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

+0

ε邻居通常不是最佳做法,而是一种懒惰的解决方法。大多数情况下,你真的不需要'==',但是'<=' or '> =',只是认为你需要'==',因为这种情况对于整数来说工作得很好,因为'''永远不会被这些情况达到。 - 另外,有时你实际上需要'=='浮点数,尽管这很少发生。 – leftaroundabout

+0

@leftaroundabout:这与我读过的大多数文章相反。你能详细说明这些情况吗?也许添加你自己的答案:) –

7

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

#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。双数据类型可以准确地表示浮点数的内容,因此比较结果为false。

相关问题