long double 자료형으로만 풀면 예제 3번처럼 높은 자릿수의 연산에서 오차가 생겨 100점을 받지 못한다.
ex)
int main() {
long double a, b, c;
cin >> a >> b >> c;
cout << setprecision(18) << fixed << a * b / c << endl;
}
결과
123456789 987654321 1 // input
121932631112635264.000000000000000000 // output (오답)
정답
121932631112635269.000000000000000000 // 5의 오차 발생
long long int로 a * b만 계산해보면
121932631112635269로 맞게 계산되지만 같은 숫자를
long double로 형변환하는 순간 오차가 생기는 것을 확인할 수 있었다.
📌 자료형 참고
| 타입 | 자료형 | 자릿수 정확도 | 안전 범위 (정수) | 특이점 |
|---|---|---|---|---|
| long double | 부동소수점 | 약 19자리 유효숫자 | 지수부는 넓지만 정수형 정확도는 제한적 | 19자리 넘어가면 근사치 (오차) 발생 |
| long long int | 정수 | 64비트 정수 | ±9 x 10^18 | 정수 연산에서 정확 (단, 오버플로우 주의) |
문제 조건상
long long int로 정수부 커버 가능하므로 정수부 + 소수부로 나누어 연산 후 결과를 출력하면 된다.
출력할 때 string으로만 더해야지 합연산하면 계산 범위 넘어서 다시 오차 생김
long long int a , b , c , ab;
ab = a * b;
cout << ab/c;
결과
123456789 987654321 7 // input (예제 4)
17418947301805038 // output (정수부)
ab/c를 통해 정수 부분의 수를 오차없이 얻어낼 수 있음
long long int res = ab % c;
cout << (long double) res/c << endl;
cout << setprecision(18) << fixed << (long double)res / c << endl;
나머지를 다시 c로 나누어서 소수부 계산을 한다.
결과
123456789 987654321 7 // input
0.428571 // output (소수부)
0.428571428571428548 // 자릿수 설정
정수부와 소수부 모두 구했으면 알맞게 출력해야하는 일이 남았다.
정수부를 출력하고 소수부에서 구한 수 0.xxx에서 0을 떼고 출력해야 우리가 원하는 정수.소수가 나올 것이다.
입출력 조작자만으로는 우리가 원하는 형태의 출력을 만들 수 없기때문에 문자열을 파싱해서 정수부와 소수부를 결합할 수 있다.
📌 하지만 문제 풀이를 찾아보면 간단한 자릿수 조작으로 이를 해결했다.
정수부의 수를 구할 때 c * 10으로 나누어 1의 자릿수 정수를 남겨두고
소수부의 수를 구할 때 그 나머지로 값을 구하는 식.
예제 4번으로 다시 예를 들어보자면
long long int a , b , c , ab;
ab = a * b;
cout << ab/(10*c) << endl; // 10 * c로 나누기
long long int res = ab % (10*c); // 10 * c로 나눈 나머지
cout << setprecision(18) << fixed << (long double)res / c << endl;
출력
123456789 987654321 7 // input
1741894730180503 // output (정수부 - 10의 자리부터)
8.428571428571428825 // output (소수부 - 1의 자리부터)
줄바꿈 없이 한 줄에 출력하면 17418947301805038.428571428571428825로 우리가 원하는 답이 나온다.
📌 조건 추가
10의 자리 정수부를 미리 출력하게되는데
ab가 10*c보다 작은 경우가 있다면 필요없는 0이 먼저 출력되기 때문에 if(ab>=(10*c)) 조건을 추가한다.
조건이 빠진 경우
5 3 2 // 5*3 < 2*10
07.500000000000000000 // 쓸모없는 숫자 0이 먼저 출력됨
📌 출력 형식
문제에서 요구하는 오차 인정 범위는 소수점 6자리이고
cout 디폴트 자릿수는 6이다.
하지만 fixed 가 false라면 정수, 소수 구분 없이 6자리로 출력하므로 출력 형식을 지정하지 않으면 오답처리가 된다.
따라서 안전하게 예제에 나온 자릿수인 18자리로 설정하거나
cout << setprecision(18) << fixed
cout << fixed만 해줘도 괜찮다. (디폴트가 6자리이므로)
#include <bits/stdc++.h>
using namespace std;
int main() {
long long int a, b, c , ab;
cin >> a >> b >> c;
ab = a*b;
if(ab >= 10*c)
{
cout << ab/(10*c);
ab %= 10*c; // res대신 ab를 덮어씀
}
cout << setprecision(18) << fixed << (long double)ab / c << endl;
}