C ++での一歩早くダブルブレイクアウトを使用してループの場合、境界値に達していません

StackOverflow https://stackoverflow.com/questions/1286394

  •  18-09-2019
  •  | 
  •  

質問

Iは32ビットのUbuntu 8.04でGCC 4.2.4を使用してコンパイルされた単純なC ++プログラムを有しています。これはfor変数が一定のステップサイズで0から1に増分された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;
}

OUTPUT

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 > 1 0.95double値によって正確に表現できないからである。

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

あなたが表示されますのIEEE浮動小数点コンバータnoreferrer"> 浮動小数点でこれを入力することによって0-01111111110-1110011001100110011001100110011001100110011001100110されていることあなたが値を取得する電卓は0.95000016で、それに0.05を追加すると1.0マークの上に表示されます。

あなたがループ内で浮動小数点を使用しない(またはより一般的には、を正確の値に浮動小数点演算の結果を比較する)んなぜ

このです。

他のヒント

一般的に、あなたがダブルスを比較する際に、単純な比較は十分ではありません、あなたは「精度まで」それらを比較する必要があります。即ち

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

問題は、二重変数のhref="http://en.wikipedia.org/wiki/Floating_point" rel="noreferrer">表現を

他の人が述べたように、

、これは、メモリ内の特定の小数の不正確な表現によく知られた問題です。私は非常に/ <すべてのコンピュータ科学者は、浮動小数点演算について知っておくべきことを読んで推薦しますA>と 数字はします。

あなたは、その内部表現のために倍増するために==や<=を使用しないでください。最後のステップでは、あなたは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ようになります。

他の人が言ったように、

、必ずしもすべての実数は、浮動小数点値として正確に表現であるので、あなたは、浮動小数点演算では、小さな、「ランダム」丸め誤差を期待することができます。 3は150及びません正確に1になるだろう*(0.33)1/3は3桁を使用して正確に表現できないので、(1/3)

:これは、通常の小数点以下の桁数で何が起こるかに似ています。

あなたの比較で「精度」のいくつかの並べ替えを使用することが可能であるが、私はループの浮動小数点数を避けることをお勧めしますし、代わりに整数を使用することになります。

たとえば、あなたのループ

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