8

32ビットUbuntu8.04でgcc4.2.4を使用してコンパイルされた単純なC++プログラムがあります。変数がゼロから特定のステップサイズで1にインクリメントされる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.95double

浮動小数点の精度についてウィキペディアが言っていることを参照してください。

IEEE浮動小数点コンバーターを見ると0.95、64ビット浮動小数点(double)の値は0-01111111110-1110011001100110011001100110011001100110011001100110、これを浮動小数点計算機に入力することで値が得られ、0.95000016それに追加するとマーク0.05を超えることがわかります。1.0

これが、ループで浮動小数点を使用してはならない理由です(または、より一般的には、浮動小数点計算の結果を正確な値と比較します)。

于 2009-08-17T06:21:05.310 に答える
11

一般に、doubleを比較する場合、単純な比較では不十分であり、「精度まで」比較する必要があります。すなわち:

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

この問題は、二重変数の表現が原因で発生します。

于 2009-08-17T06:24:44.553 に答える
7

内部表現のため、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;
}

詳細については、すべてのコンピューター科学者が浮動小数点演算について知っておくべきことをお読みください。

于 2009-08-17T06:21:44.793 に答える
6

他の人が述べているように、これはメモリ内の特定の10進数の不正確な表現によるよく知られた問題です。すべてのコンピューター科学者が浮動小数点演算と実数のIEEE浮動小数点表現について知っておくべきことを読むことを強くお勧めします。

于 2009-08-17T06:30:36.853 に答える
4

ほとんどの正確な小数は、浮動小数点演算で正確な有限表現を持っていません。

ゴールドバーグのすべてのコンピューター科学者が浮動小数点演算について知っておくべきことを読む必要があります。

于 2009-08-17T06:32:18.020 に答える
3

他の人が言っているように、すべての実数が浮動小数点値として正確に表現できるわけではないため、浮動小数点の計算では小さな「ランダムな」丸め誤差が予想されます。これは、通常の10進数で発生することと似ています。1/ 3は3桁の10進数(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;
}
于 2009-08-17T07:13:40.230 に答える
2

おそらく最後のindex値はのようになります1.00000001

于 2009-08-17T06:21:47.057 に答える
1

これは、浮動小数点数による小数の表現が不正確であるためです。ステップサイズは実際には0.1または0.05ではなく、非常に近い他の値です。ループを通過するにつれて、わずかなエラーが蓄積されます。

この問題を解決するには、浮動小数点数が等しいかどうかを比較することを避ける必要があります。

于 2009-08-17T06:31:08.410 に答える
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

前の回答のように、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; 



}
于 2017-02-05T19:07:03.460 に答える