我在32位Ubuntu 8.04上使用gcc 4.2.4编译了一个简单的C ++程序。它有一个for
- 循环,其中double
变量从零增加到一个具有特定步长的变量。当步长为0.1
时,行为就是我的预期。但是当步长为'0.05'时,循环在0.95
之后退出。谁能告诉我为什么会这样?输出遵循以下源代码。
#include <iostream>
using namespace std;
int main()
{
double rangeMin = 0.0;
double rangeMax = 1.0;
double stepSize = 0.1;
for (double index = rangeMin; index <= rangeMax; index+= stepSize)
{
cout << index << endl;
}
cout << endl;
stepSize = 0.05;
for (double index = rangeMin; index <= rangeMax; index+= stepSize)
{
cout << index << endl;
}
return 0;
}
输出
sarva@savija-dev:~/code/scratch$ ./a.out
0
0.1
0.2
0.3
0.4
0.5
0.6
0.7
0.8
0.9
1
0
0.05
0.1
0.15
0.2
0.25
0.3
0.35
0.4
0.45
0.5
0.55
0.6
0.65
0.7
0.75
0.8
0.85
0.9
0.95
sarva@savija-dev:~/code/scratch$
答案 0 :(得分:18)
使用浮点值时,并非每个值都可以准确表示0.95+0.05 > 1
,因为0.95
不能用double
值完全表示。
了解维基百科对floating point accuracy的看法。
如果查看IEEE floating point converter,您会看到64位浮点(0.95
)中double
的值为0-01111111110-1110011001100110011001100110011001100110011001100110
,请在floating point calculator中输入0.95000016
{3}}您获得的值为0.05
,并添加1.0
会使您超过{{1}}标记。
这就是为什么你不应该在循环中使用浮点数(或者更一般地将浮点计算的结果与精确值进行比较)。
答案 1 :(得分:11)
通常,当你比较双打时,简单的比较不够好,你应该比较它们“达到精度”。即:
if ( fabs(double1-double2) < 0.0000001 ) {
do-something
}
答案 2 :(得分:6)
由于其内部表示,您不应使用==或&lt; = for double。在最后一步,您将获得0.95000000000000029
。相反,您可以使用以下代码:
stepSize = 0.05;
// stepSize/2 looks like a good delta for most cases
for (double index = rangeMin; index < rangeMax+stepSize/2; index+= stepSize)
{
cout << index << endl;
}
有关详细信息,请参阅What Every Computer Scientist Should Know About Floating-Point Arithmetic。
答案 3 :(得分:6)
正如其他人所提到的,由于内存中某些十进制数字的不精确表示,这是一个众所周知的问题。我强烈建议您阅读What Every Computer Scientist Should Know About Floating-Point Arithmetic和IEEE floating-point representations of real numbers。
答案 4 :(得分:4)
大多数精确小数在浮点运算中没有精确的有限表示。
您需要阅读Goldberg的What Every Computer Scientist Should Know About Floating-Point Arithmetic。
答案 5 :(得分:2)
最后index
值可能就像1.00000001
。
答案 6 :(得分:2)
正如其他人所说,并非每个实数都可以完全表示为浮点值,因此您可以预期浮点计算中会出现一个小的“随机”舍入误差。它类似于普通十进制数字所发生的情况:使用三位十进制数字(0.33)不能完全表示1/3,因此(1/3)* 3将变为0.99而不是完全为1。
在比较中可以使用某种“精度”,但我建议避免使用循环的浮点数,而是使用整数。
例如,你的循环
stepSize = 0.05;
for (double index = rangeMin; index <= rangeMax; index+= stepSize)
{
cout << index << endl;
}
可以替换为
的内容stepSize = 0.05;
for (int index = 0; index < 21; ++index)
{
double value = rangeMin + index * stepSize;
cout << value << endl;
}
答案 7 :(得分:1)
这是由于浮点数表示小数部分的不精确表示。你的步长实际上不是0.1或0.05,它是一些非常接近的其他值。在循环中累积了轻微的误差。
要解决此问题,必须避免将浮点数进行比较。
答案 8 :(得分:1)
请参阅此输出:(浮点精度)
#include <iostream>
#include <iomanip>
using namespace std;
int main(){
double rangeMin = 0.0;
double rangeMax = 1.0;
double stepSize = 0.1;
double index;
for (index = rangeMin; index <= rangeMax; index+=stepSize)
{
cout << fixed << setprecision(16) << index << endl;
}
cout << endl;
stepSize = 0.05;
for (index = rangeMin; index<= rangeMax; index+= stepSize)
{
cout << index << endl;
}
cout << "\n" << setprecision(16) << index << " " << rangeMax;
if(index==rangeMax)
cout << "\nEQ";
else
cout << "\nNot EQ";
return 0;
}
0.0000000000000000
0.1000000000000000
0.2000000000000000
0.3000000000000000
0.4000000000000000
0.5000000000000000
0.6000000000000000
0.7000000000000000
0.7999999999999999
0.8999999999999999
0.9999999999999999
0.0000000000000000
0.0500000000000000
0.1000000000000000
0.1500000000000000
0.2000000000000000
0.2500000000000000
0.3000000000000000
0.3500000000000000
0.4000000000000000
0.4500000000000000
0.4999999999999999
0.5499999999999999
0.6000000000000000
0.6500000000000000
0.7000000000000001
0.7500000000000001
0.8000000000000002
0.8500000000000002
0.9000000000000002
0.9500000000000003
1.0000000000000002 1.0000000000000000
Not EQ
答案 9 :(得分:1)
如前所述,在for循环中使用非整数是不准确的,所以我建议按以下示例进行操作,以便保持整数的准确性,并且可以获得所需的小数:
#include<iostream>
#include<cmath>
#include<iomanip>
using namespace std;
int main()
{
for (double y = 1; y!=10; y += 1)
cout << static_cast<double>(y/10) << endl;
}