문제

32 비트 우분투 8.04에서 GCC 4.2.4를 사용하여 간단한 C ++ 프로그램을 컴파일했습니다. 그것은 있습니다 for-루프 a double 변수는 특정 단계 크기로 0에서 1로 증가합니다. 단계 크기가있을 때 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 > 1 왜냐하면 0.95 정확히 표현할 수 없습니다 double 값.

Wikipedia가 무엇을 말하는지 확인하십시오 플로팅 포인트 정확도.

당신이 보면 IEEE 플로팅 포인트 변환기 당신은 그 가치를 볼 수 있습니다 0.95 64 비트 플로팅 포인트 (double) 이다 0-01111111110-1110011001100110011001100110011001100110011001100110 이것을 입력하여 a 플로팅 포인트 계산기 당신은 가치를 얻습니다 0.95000016 그리고 추가 0.05 그것에 당신을 데려갑니다 1.0 표시.

이것이 루프에서 부동 소수점을 사용해서는 안되는 이유입니다 (또는 더 일반적으로 부동 소수점 계산 결과를 정확한 값).

다른 팁

일반적으로 복식을 비교할 때 간단한 비교만으로는 충분하지 않으며 "정밀도로"비교해야합니다. 즉 :

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

문제가 발생합니다 이중 변수의 표현.

다른 사람들이 언급했듯이, 이것은 메모리에서 특정 소수점 숫자의 부정확 한 표현으로 인해 잘 알려진 문제입니다. 나는 읽기를 강력히 추천합니다 모든 컴퓨터 과학자가 부동 소수점 산술에 대해 알아야 할 것 그리고 실수의 IEEE 부동 소수점 표현.

내부 표현으로 인해 복식에 == 또는 <=를 사용해서는 안됩니다. 마지막 단계에서 얻을 수 있습니다 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;
}

자세한 내용은 참조하십시오 모든 컴퓨터 과학자가 부동 소수점 산술에 대해 알아야 할 것.

대부분의 정확한 소수점은 플로팅 포인트 산술에서 정확한 유한 표현이 없습니다.

Goldberg 's를 읽어야합니다 모든 컴퓨터 과학자가 부동 소수점 산술에 대해 알아야 할 것.

아마 마지막 index 가치는 같을 것입니다 1.00000001.

다른 사람들이 말했듯이, 모든 실수가 부동 소수점 값으로 정확하게 표현되는 것은 아니므로 부동 소수점 계산에서 작은 "무작위"반올림 오류를 기대할 수 있습니다. 그것은 정상적인 소수점 자릿수에서 발생하는 것과 유사합니다. 1/3은 3 자리 숫자 (0.33)를 사용하여 정확하게 표현할 수 없으므로 (1/3)*3은 정확히 1이 아닌 0.99가됩니다.

비교에서 일종의 "정밀도"를 사용할 수는 있지만 루프의 부동 소수점 번호를 피하고 대신 정수를 사용하는 것이 좋습니다.

예를 들어, 루프

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가 아니며 매우 가까운 다른 값입니다. 루프를 통과 할 때 약간의 오류가 축적됩니다.

이 문제를 해결하려면 평등에 대한 부동 소수점 숫자를 비교하지 않아야합니다.

이 출력을 참조하십시오 : (플로팅 포인트 Accuarcy)

#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

이전 답변으로, 루프에서 비 integers를 사용하는 것이 정확하지 않으므로 다음 예제로 수행하는 것이 좋습니다. 따라서 정수의 정확성을 유지하면 원하는 소수점을 얻을 수 있습니다.

#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