<문제 출처>
https://swexpertacademy.com/main/code/problem/problemDetail.do
<문제 풀이 과정>
<Fail - 1>
#include <stdio.h>
int main()
{
int t;
scanf("%d", &t);
int N, num;
double total;
for (int i = 0; i < t; i++)
{
total = 0;
scanf("%d", &N);
num = sizeof(N) / sizeof(int);
if (num > 3)
{
for (int j = 0; j < num - 3; j++)
{
N /= 10;
}
}
if (N % 10 >= 5)
{
N = N / 10 + 1;
if (N % 10 == 0)
{
total = (double)N / 100;
printf("#%d %.1f*10^%d\n", i + 1, total, num + 1);
continue;
}
else
total = (double)N / 10;
}
printf("#%d %.1f*10^%d\n", i + 1, total, num);
}
return 0;
}
처음 제가 풀었던 코드입니다. 아마, sizeof연산자를 사용한 이유는 하나의 수 N이 19999라 한다면 앞에 3개의 수 199만을 분리하기 위함이었던 것 같습니다. 그러나 이는 sizeof연산자의 올바른 사용이 아니고, 출력 값 또한 num =1과 같이 나올 뿐입니다. sizeof연산자는 자료형의 크기를 바이트 단위로 구하는 연산자입니다.
<sizeof연산자>
#include<stdio.h>
int main()
{
int num = 5678;
int arr[4] = { 1,2,3,4};
printf("%d %d %d\n",sizeof(num),sizeof(int),sizeof(num)/sizeof(int) );
printf("%d %d %d\n", sizeof(arr), sizeof(int), sizeof(arr) / sizeof(int));
return 0;
}
sizeof연산자는 배열의 길이를 구할때 사용이 될 수 있습니다.
<Fail -1> 의 코드는 전체적으로 잘못 짜인 코드이며 틀린 이유가 sizeof연산자의 오용에만 있지는 않습니다.
<Fail-2>
문제에서 볼 수 있다시피, 각 케이스마다하나의 정수 N이 주어집니다. ( 10^2≤ N ≤ 10^100000)
N의 최댓값은 10의 10만 승인데, 이는 정말 어마어마한 수이고, int나 long long int(900경) , unsigned long long int 등 의 자료형을 가지고는 이 수를 담을 수 없습니다.
1경은 10의 16승 이고, 10의 18승이 100경, 10의 20승은 1해입니다.
그러므로 문자열을 이용해 문제를 풀어줘야 합니다.
문자열 arr의 길이는 100002으로 설정하여 줍니다. ( 왜냐! 10의 1승은 2자리, 10의 3승은 4자리 소요, 그러면 10의 만승은 10001자리 수가 나오고, 널문자까지 포함하여서 배열의 길이(문자열의 길이)가 10002자리입니다.)
#include<stdio.h>
#include<math.h>
#include<string.h>
int main(void)
{
int t;
scanf("%d", &t);
int sum;
double total;
char arr[100002];
for (int i = 0; i < t; i++)
{
scanf("%s", arr);
sum = (arr[0] - 48) * 100 + (arr[1] - 48) * 10 + (arr[2] - 48);
total = (double)sum * pow(10, -2);
if (sum % 10 == 9)
{
sum /= 10;
if(sum%10==9)
printf("#%d 1.0*10^%d\n", i + 1, strlen(arr));
else
printf("#%d %.1f*10^%d\n", i + 1, total, strlen(arr) - 1);
}
else
printf("#%d %.1f*10^%d\n", i + 1, total, strlen(arr) - 1);
}
return 0;
}
문자열을 이용해서 수를 입력받아 코드를 구현하였지만, 반례가 존재하게 됩니다. 이 반례들을 찾아서 여러 코드를 다시 구현했지만 계속 Fail이 나왔습니다. 이 반례들이 존재하는 이유는 코드에 부족함이 있는 것도 맞지만, 실수 자료형을 사용하고 실수를 표현하는 데에 있어서 정수와는 달리 오차가 존재하기 때문이라고 생각하였고, 실수 자료형들을 사용하지 않고 int형으로만 코드를 구현해보았습니다.
<Pass>
#include<stdio.h>
#include<string.h>
int main(void)
{
int t;
scanf("%d", &t);
char arr[100002];
int sum = 0,A,B,C;
for (int i = 0; i < t; i++)
{
scanf("%s", arr);
A = arr[0] - 48;
B = arr[1] - 48;
C = arr[2] - 48;
sum = A * 100 + B * 10 + C;
if (A == 9)
{
if (B == 9)
{
if (C >= 5)
{
printf("#%d 1.0*10^%d\n", i + 1, strlen(arr));
}
else
{
printf("#%d 9.9*10^%d\n", i + 1, strlen(arr)-1);
}
}
else
{
if (C >= 5)
{
printf("#%d 9.%d*10^%d\n", i + 1,B+1, strlen(arr) - 1);
}
else
{
printf("#%d 9.%d*10^%d\n", i + 1,B, strlen(arr) - 1);
}
}
}
else
{
if (B == 9)
{
if (C >= 5)
{
printf("#%d %d.0*10^%d\n",i+1,A+1,strlen(arr)-1);
}
else
{
printf("#%d %d.%d*10^%d\n", i + 1, A, B , strlen(arr) - 1);
}
}
else
{
if (C >= 5)
{
printf("#%d %d.%d*10^%d\n", i + 1, A, B+1, strlen(arr) - 1);
}
else
{
printf("#%d %d.%d*10^%d\n", i + 1, A, B, strlen(arr) - 1);
}
}
}
}
return 0;
}
if else 문을 사용해 반올림이 될 경우들을 세세히 나누어 주었고, 각 조건에 맞는 출력이 실행되게끔 구현을 하였습니다.
유효숫자를 표기하는 문제로 무조건 실수 자료형을 사용해야 할 것 같았지만, 그렇지 않았습니다. 반올림이라는 키 포인트가 있기 때문에 실수 자료형을 사용했지만, 오히려 반올림하는 데에 있어서 오차가 있는 실수 자료형을 이와 같은 문제에서는 사용을 하면 안 된다는 것을 깨닫게 되었습니다.
< 실수 자료형 float형 double형 long double형 >
float형은 단정밀도 부동소수점 변수이고 , double형과 long double형은 배정밀도 부동소수점 변수입니다.
단정밀도 부동소수점은 유효 자릿수 7자리까지 저장할 수 있으며, 배정밀도 부동소수점이 좀 더 긴 자릿수의 소수점을 정밀하게 표현할 수 있습니다.
그렇지만 부동소수점 방식은 실수를 정확히 표현할 수 없는 문제가 있고, 그렇다면 반올림을 하는데에 있어서도 충분히 반올림 오차가 발생할 가능성이 있다는 것입니다. 아래 공부하고 있는 교재의 출처를 같이 첨부해 보았습니다.
https://dojang.io/mod/page/view.php?id=738