소수점 고정 시 반올림 오류
#include <iostream>
using namespace std;
int main() {
cout << fixed;
cout.precision(1);
cout << 3.15 << '\n'; // 3.1
cout << 4.15 << '\n'; // 4.2
}
cout << fixed;와 cout.precision()을 사용해서 소수점 고정 시 예측과 다른 값이 나오는 경우가 있다. 이는 C++의 반올림 방식 때문이다. C++의 기본적인 반올림 방식은 반올림 오차로 인해 예상과 다를 수 있다.
C++의 cout 객체와 precision 설정은 반올림 오차가 발생할 수 있는 방법으로 동작한다. 이는 숫자 내부 표현 방식과 관련이 있다. 실수는 이진 부동 소수점 형식으로 저장되기 때문에 10진수에서 정확하게 표현할 수 없는 경우가 생길 수 있다.
- 3.15는 3.1로 출력된다.
- 이는 반올림 오차로 인해 3.149999... 와 같이 표현될 수 있고, 소수 첫째 자리까지 표시할 때 3.1로 반올림 된다.
- 4.15는 4.2로 출력된다.
- 이 경우 4.1500000... 과 같이 표현되어 소수 첫째 자리까지 표시할 때 4.2로 반올림 된다.
부동 소수점 방식 오차
부동 소수점 방식은 고정 소수점 방식보다 훨씬 더 많은 범위까지 표현할 수 있지만, 항상 오차가 존재한다는 단점을 가지고 있다. 반올림 오차는 이러한 부동 소수점 방식의 오차 때문에 생긴다. 부동 소수점 방식은 실수를 근사치로 표현하기 때문에, 정확하게 표현할 수 없는 수가 발생한다. 이러한 근사치 표현은 종종 예상치 못한 반올림 결과를 초래할 수 있다. 부동 소수점 방식은 이진수로 실수를 표현하는데, 일부 10진 소수는 이진 소수로 정확하게 표현할 수 없다. 예를 들어, 0.1이나 0.2 같은 수는 이진 소수로 무한히 반복되는 수가 되어버리기 때문에 정확하게 저장되지 않는다. 이러한 이유로 인해 부동 소수점 연산에서는 미세한 오차가 발생할 수 있다.
즉, 부동 소수점 방식을 사용하기 때문에 컴퓨터는 10진수를 정확하게 표현할 수는 없다. (가수부가 표현할 수 있는 비트 수를 넘어가게 되면 손실되는 부분이 생기기 때문이다. 실수 또한 이진수로 표현하기 때문에 가수부가 1/2^n 꼴로 표현되는 경우만 오차없이 정확하게 값이 계산된다.)
다시 돌아가서, 정확히 어떤 반올림 방식이 동작하는지 보기 위해, 좀 더 정밀하게 출력하는 것이 도움이 된다. 이를 위해 cout.precision() 값을 높여서 확인해볼 수 있다.
#include <iostream>
using namespace std;
int main() {
cout << fixed;
cout.precision(10); // 정밀도를 높여서 출력
cout << 3.15 << '\n'; // 3.1500000000
cout << 4.15 << '\n'; // 4.1500000000
}
디버깅을 했을 때 결과가 잘 나와도, 컴퓨터 내부에서는 3.149999와 같이 표현되어 있을 수 있다고 한다. 부동소수점은 실수를 근사치로 표현하기 때문이다. 이런 상황을 피하기 위해 매우 작은 수를 더해주는 트릭을 이용하거나, 반올림 함수를 직접 구현하는 등의 방법이 있다.
#include <iostream>
#include <cmath>
using namespace std;
double round_to_nearest_tenth(double value) {
return round(value * 10) / 10;
}
int main() {
cout << fixed;
cout.precision(1);
cout << round_to_nearest_tenth(3.15) << '\n'; // 3.2
cout << round_to_nearest_tenth(4.15) << '\n'; // 4.2
}
위 코드는 주어진 값을 소수점 첫째 자리로 반올림하여 정확한 결과를 출력한다. round()함수는 가장 가까운 정수로 반올림 하는데, 이를 활용하여 소수 첫째 자리로 반올림할 수 있다. 다만, 이것도 첫째자리까지 잘라서 사용하거나 하는 게 좋은 게, 입력에 3.149999999를 넣으면 3.2가 나온다. 부동 소수점에 주의하면서 반올림을 해줘야겠다. 소수점을 최대한 적게 사용하거나
'C++' 카테고리의 다른 글
[C++] for loop에서 const auto&의 사용 (0) | 2024.12.01 |
---|---|
[C++] assign과 resize의 차이 (1) | 2024.11.10 |
[C++] 소수점 고정 출력과 cout << fixed, cout << setprecision(), cout.precision() (0) | 2024.08.13 |
[C++] cin과 getline(), 그리고 cin.ignore() (0) | 2024.08.13 |
[C++] 참조(reference)와 복사(copy)의 차이 (0) | 2024.07.25 |