작성 언어
C++
문제
두 자연수 a, b를 입력받아 a / b 결과를 소수점 20째자리까지 출력하는 프로그램을 작성하시오
테스트 케이스
입력
3 5
출력
0.60000000000000000000
입력
3 7
출력
0.42857142857142857142
풀이
단순히 나눗셈 연산으로는 20째자리까지 정밀하게 계산할 수 없으리라 판단하고 직접 나눗셈의 원리를 적용하여 풀었다.
#include <iostream>
#include <cmath>
using namespace std;
int main() {
int a = 0, b = 0;
cin >> a >> b;
int quotient = 0;
double result = 0.0;
for (int i = 0; i < 20; i++) {
quotient = a / b;
result += pow(10, -i) * quotient; // 소숫점 i째자리 값으로 넣어주기
a = a % b;
a *= 10;
}
cout << fixed;
cout.precision(20);
cout << result << endl;
return 0;
}
나눗셈은 몫과 나머지를 이용해서 이루어진다. 에를 들어 3을 7로 나눌 경우, 몫은 0 나머지는 3이다. 나머지가 나누는 값보다 작으므로 나머지에 10을 곱해준다. 이제 나머지는 30 나누는 값은 7이므로 몫은 4, 나머지는 2이다. 이때 10을 곱해주었으므로 다시 10으로 나누어주면 전체 몫은 0.4XXXXX(X는 위와 같은 단계로 연산을 한 결과)가 된다.
이 논리를 그대로 적용한 것이 위 코드와 같다.
문제점
실행한 결과 앞부분까지는 잘 출력되나 소수점 아래 17번째 자리부터는 다른 값이 나왔다.
연산을 진행하면서 매우 작은 값에서는 정밀도가 떨어지는 것을 발견했다. 부동소수점의 문제인지 정확히 원인은 파악할 수 없으나 자료형의 문제로 판단해 result를 double 대신 string으로 수정해주었다. 그때그때 업데이트되는 숫자 하나를 그대로 string에 넣어주면 값이 바뀌는 에러가 없을 것이다.
수정한 코드
#include <iostream>
#include <string>
using namespace std;
int main() {
int a = 0, b = 0;
cin >> a >> b;
int quotient = 0;
string result = to_string(a / b) + ".";
a %= b;
for (int i = 0; i < 20; i++) {
a *= 10;
quotient = a / b;
result += to_string(quotient);
a = a % b;
}
cout << result << endl;
return 0;
}
a / b를 나눈 값(정수)를 string으로 변환하고 뒤에 .을 추가해주어서 X. 형태로 만들어준다.
string으로 변환해주는 부분을 제외하면 로직 자체는 처음 짠 코드와 동일하다.
알게된 점
실수(float, double)을 계산할 때 아주 작은 범위에서는 계산에 오차가 있을 수 있음을 상기하자