8

我有一个在 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$
4

10 回答 10

20

使用浮点值时,并非每个值都可以精确表示,0.95+0.05 > 1因为0.95不能用double值精确表示。

看看维基百科对浮点精度的看法。

如果您查看IEEE 浮点转换器,您会看到0.9564 位浮点 ( double) 中的值是0-01111111110-1110011001100110011001100110011001100110011001100110通过在浮点计算器中输入的,您得到的值是0.95000016,加上0.05它会使您超过1.0标记。

这就是为什么你永远不应该在循环中使用浮点数(或者更普遍地将浮点计算的结果与精确值进行比较)。

于 2009-08-17T06:21:05.310 回答
11

一般来说,当你比较双打时,简单的比较是不够的,你应该“精确地”比较它们。IE :

if ( fabs(double1-double2) < 0.0000001 ) {
  do-something
}

该问题是由于双变量的表示而发生的。

于 2009-08-17T06:24:44.553 回答
7

由于其内部表示,您不应将 == 或 <= 用于双精度。在最后一步你会得到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;
}

有关更多详细信息,请阅读每位计算机科学家应了解的浮点运算知识

于 2009-08-17T06:21:44.793 回答
6

正如其他人所提到的,这是一个众所周知的问题,因为内存中某些十进制数的表示不准确。我强烈推荐阅读What Every Computer Scientist Should Know About Floating-Point ArithmeticIEEE floating-point representations of real numbers

于 2009-08-17T06:30:36.853 回答
4

大多数精确小数在浮点算术中没有精确的有限表示。

您需要阅读 Goldberg 的What Every Computer Scientist Should Know About Floating-Point Arithmetic

于 2009-08-17T06:32:18.020 回答
3

正如其他人所说,并非每个实数都可以精确地表示为浮点值,因此您可以在浮点计算中预期一个小的“随机”舍入误差。这与普通十进制数字发生的情况类似:使用三个十进制数字 (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;
}
于 2009-08-17T07:13:40.230 回答
2

可能最后一个index值是1.00000001.

于 2009-08-17T06:21:47.057 回答
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
于 2009-08-17T06:40:40.733 回答
1

这是由于浮点数不精确地表示小数。您的步长实际上不是 0.1 或 0.05,而是非常接近的其他值。当您通过循环时,轻微的错误会累积。

要解决此问题,您必须避免比较浮点数是否相等。

于 2009-08-17T06:31:08.410 回答
1

正如前面的答案,在 for-loops 中使用非整数是不准确的,所以我建议按照下面的例子做,这样你就可以保持整数的准确性,你可以得到你想要的小数:

#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; 



}
于 2017-02-05T19:07:03.460 回答