我有一个简单的C++的程序编制使用的海湾合作委员会4.2.4在32位Ubuntu8.04.它有一个 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.95+0.05 > 10.95不是由double值精确表示。

看看维基百科不得不说的浮点精度

如果你看一下 IEEE浮点转换你会看到该0.95的在64位浮点(double)的值是通过在浮点进入该0-01111111110-1110011001100110011001100110011001100110011001100110 你得到的值计算器0.95000016并添加0.05到需要你过1.0大关。

这就是为什么你不应该在环路使用浮动点(或更一般地比较浮点计算到的确切值的结果)。

其他提示

一般情况下,当你比较双打,简单的比较是不够好,你应该对它们进行比较“达精密”。即:

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

的发生是由于双变量中的表示此问题。

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

有关详细信息,请参阅什么每台计算机科学家应该知道关于浮点运算

最确切小数不必在浮点运算的精确有限表示。

您需要阅读戈德堡的什么每台计算机科学家应该知道关于浮点算术

可能是最后index值会像1.00000001

正如其他人所说,并不是每一个实数是作为一个浮点值精确表示的,所以你可以期待一个小的,“随机”舍入浮点运算错误。这是类似于正常十进制数字发生:1/3不是精确表示使用三个十进制数字(0.33),因此,(1/3)* 3将成为0.99和不完全1

有可能使用某种在你比较“精确”,但我会建议避免for循环浮点数,而是使用整数。

例如,你的循环

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

这是由于浮点数小数的不精确表示。你的步长实际上不是0.1或0.05,它是非常接近的其他值。当您去通过循环略有误差积累。

要解决这个问题,必须避免对平等比较浮点数。

请参阅下面的输出:(浮点精度)

#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

由于以前的答案,这是不准确的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; 



}
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top