【问题标题】:For-loop in C++ using double breaking out one step early, boundary value not reachedC ++中的for循环使用双重提前一步,未达到边界值
【发布时间】:2010-11-20 03:50:17
【问题描述】:

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

【问题讨论】:

  • 可能是浮点问题,我要检查!!!
  • 不要马上接受答案!给人们一些时间来回答您的问题并输入答案。

标签: c++ for-loop floating-accuracy


【解决方案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; 



}

【讨论】:

    【解决方案2】:

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

    看看维基百科对floating point accuracy的评价。

    如果您查看IEEE floating point converter,您会看到0.95 在64 位浮点(double) 中的值是0-01111111110-1110011001100110011001100110011001100110011001100110,通过在floating point calculator 中输入这个值,您会得到值是@ 987654330@ 并在其中添加 0.05 即可让您超越 1.0 标记。

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

    【讨论】:

    • 在这个特定示例中,更好的解决方案可能是将index 计算为整数,然后比较index * stepSize
    【解决方案3】:

    正如其他人所说,并非每个实数都可以精确地表示为浮点值,因此您可以预期浮点计算中会出现小的“随机”舍入误差。这与普通十进制数字的情况类似:1/3 不能用三个十进制数字 (0.33) 精确表示,因此 (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;
    }
    

    【讨论】:

      【解决方案4】:

      由于其内部表示,您不应将 == 或 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

      【讨论】:

        【解决方案5】:

        看到这个输出:(浮点精度)

        #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
        

        【讨论】:

          【解决方案6】:

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

          你需要阅读戈德堡的What Every Computer Scientist Should Know About Floating-Point Arithmetic

          【讨论】:

            【解决方案7】:

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

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

            【讨论】:

              【解决方案8】:

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

              【讨论】:

                【解决方案9】:

                通常,当您比较双精度时,简单的比较是不够的,您应该“精确地”比较它们。即:

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

                由于representation of double variables而出现问题。

                【讨论】:

                  【解决方案10】:

                  可能最后一个index 值类似于1.00000001

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2021-08-18
                    • 2020-03-09
                    • 1970-01-01
                    • 1970-01-01
                    • 2021-07-12
                    • 1970-01-01
                    相关资源
                    最近更新 更多