[ft_printf](10)반올림 옵션 구현하기
이번 포스트는 (ft_print)반올림 옵션 구현하기에 관한 내용입니다.
1️⃣ 실수형 서식지정자 규칙
(1) %f 규칙
int main(void)
{
int a,b,c,d,e,f,g,h,i,j,k;
int aa,bb,cc,dd,ee,ff,gg,hh,ii,jj,kk,ll;
a = printf("(a)%*.*f끝\n", 11, 4, 12.34);
b = printf("(b)% *.*f끝\n", 11, 4, 12.34);
c = printf("(c)%+*.*f끝\n", 11, 4, 12.34);
d = printf("(d)%*.*f끝\n", +11, 4, 12.34);
e = printf("(e)%-*.*f끝\n", 11, 4, 12.34);
f = printf("(f)%*.*f끝\n", -11, 4, 12.34);
g = printf("(g)%*f끝\n", 11, 12.34);
h = printf("(h)%.*f끝\n", 11, 12.34);
i = printf("(i)%11.*f끝\n", 4, 12.34);
j = printf("(j)%*.4f끝\n", 11, 12.34);
k = printf("(k)%0*f끝\n", 11,12.34);
aa = printf("(aa)%f끝\n", 12.34);
bb = printf("(bb)%11f끝\n", 12.34);
cc = printf("(cc)%+11f끝\n", 12.34);
dd = printf("(dd)%+11f끝\n", -12.34);
ee = printf("(ee)% 11f끝\n", -12.34);
ff = printf("(ff)% -11f끝\n", -12.34);
gg = printf("(gg)%-11f끝\n", 12.34);
hh = printf("(hh)% -11f끝\n", 12.34);
ii = printf("(ii)%011f끝\n", -12.34);
jj = printf("(jj)% 011f끝\n", 12.34);
kk = printf("(kk)% 11.4f끝\n", 12.34);
ll = printf("(ll)%#f끝\n", 12.34);
}
(a) 12.3400끝
(b) 12.3400끝
(c) +12.3400끝
(d) 12.3400끝
(e)12.3400 끝
(f)12.3400 끝
(g) 12.340000끝
(h)12.34000000000끝
(i) 12.3400끝
(j) 12.3400끝
(k)0012.340000끝
(aa)12.340000끝
(bb) 12.340000끝
(cc) +12.340000끝
(dd) -12.340000끝
(ee) -12.340000끝
(ff)-12.340000 끝
(gg)12.340000 끝
(hh) 12.340000 끝
(ii)-012.340000끝
(jj) 012.340000끝
(kk) 12.3400끝
(ll)12.340000끝
- %f 서식자는 모든 옵션과 플래그사용이 가능했습니다. 기본적으로 소수점 6번째 자리까지 출력을 해줍니다.
- %f 서식지정자에서
.xx
소수옵션은 정밀도로써 기능을 합니다. 소수점 아래의 수만큼 소수부분의 출력을 결정해 줍니다.
< #플래그 >
printf("%#.0f끝\n", 12.34);
printf("%.0f끝\n", 12.34);
12.끝
12끝
- #플래그는 이 경우에서만 유효한 기능을 했습니다.(
.0
정밀도일경우.
를 표현해줍니다.))
(2) %g 규칙
< %g 기본 규칙 >
printf("%-8.6g끝\n", 12.34);
printf("%8.6g끝\n", 12.34);
printf("%#8.6g끝\n", 12.34);
printf("%+8.6g끝\n", 12.34);
printf("% 8.6g끝\n", 12.34);
12.34 끝
12.34끝
12.3400끝
+12.34끝
12.34끝
- %g서식자의 경우 정밀도옵션이 %f서식자와는 다르게 정수부분부터 관리를 해줬습니다.(‘ . ‘점 제외)
- 또한 정밀도옵션의 값대로 모두 출력되는 것이 아닌, 범위내에서 소수점아래가
0
이라면 생략하였습니다.
< %g(0생략 규칙) 예시 >
printf("%8.6g끝\n", 12.3401);
printf("%8.6g끝\n", 12.34001); // 정밀도가 6이므로 12.3400까지만 인식
12.3401끝
12.34끝
- %g서식자는 #플래그를 사용하면 정밀도값만큼을 모두 출력해줍니다. 또한 %f와 마찬가지로 소수점에 걸치게 출력된다면 ` . `(점)까지도 출력해줍니다.
- 하지만 %g서식자의 정밀도옵션같은 경우 정수부분의 갯수까지도 고려하게 됩니다. 만약 정밀도의 값이 정수 자리수보다 작게된다면 다음과 같이 출력됩니다.
< %g(정밀도값 < 정수자리수) 예시 >
printf("%.3g끝\n", 327.555); // 소수첫째자리에서 반올림 적용
printf("%#.3g끝\n", 327.555);
printf("%.2g끝\n", 327.555); // 일의자리에서 반올림 적용
printf("%#.2g끝\n", 327.555);
printf("%.1g끝\n", 327.555);
printf("%#.1g끝\n", 327.555);
printf("%.0g끝\n", 327.555);
printf("%#.0g끝\n", 327.555);
printf("%.1g끝\n", 3.555); // 정수부분이 한자리 일때 .1 .0 비교
printf("%#.1g끝\n", 3.555);
printf("%.0g끝\n", 3.555);
printf("%#.0g끝\n", 3.555);
328끝
328.끝
3.3e+02끝
3.3e+02끝
3e+02끝
3.e+02끝
3e+02끝
3.e+02끝
4끝
4.끝
4끝
4.끝
- 정밀도값 < 정수자리수 가 된 순간 정수부분이
x.x
꼴로 맞춰지고e+xx
로 정수부분 자리수를 표현해 주었습니다. - 경계에서 반올림이 적용되었습니다.
- 만약
.0
의 경우 일지라도 최소 한개의 숫자가 표현 됩니다. 즉,.1
과 같이 동작했습니다.
(3) %e 규칙
< %e 규칙 >
printf("%-8.6e끝\n", 12.34);
printf("%8.6e끝\n", 12.34);
printf("%#8.6e끝\n", 12.34);
printf("%8.6e끝\n", 12.3401);
printf("%8.6e끝\n", 12.34001);
printf("%+8.6e끝\n", 12.34);
printf("% 8.6e끝\n", 12.34);
printf("%.3e끝\n", 327.555);
printf("%#.3e끝\n", 327.555);
printf("%.2e끝\n", 327.555);
printf("%#.2e끝\n", 327.555);
printf("%.1e끝\n", 327.555);
printf("%#.1e끝\n", 327.555);
printf("%.0e끝\n", 327.555);
printf("%#.0e끝\n", 327.555);
printf("%.1e끝\n", 3.555);
printf("%#.1e끝\n", 3.555);
printf("%.0e끝\n", 3.555);
printf("%#.0e끝\n", 3.555);
1.234000e+01끝
1.234000e+01끝
1.234000e+01끝
1.234010e+01끝
1.234001e+01끝
+1.234000e+01끝
1.234000e+01끝
3.276e+02끝
3.276e+02끝
3.28e+02끝
3.28e+02끝
3.3e+02끝
3.3e+02끝
3e+02끝
3.e+02끝
3.6e+00끝
3.6e+00끝
4e+00끝
4.e+00끝
- %e서식자의 정밀도 옵션은 기본적으로 %f서식자와 동일하게 작동했습니다.
- 정수부분이
x.x
꼴로 맞춰지고e+xx
로 정수부분 자리수를 표현해 주었습니다. - %g서식자에서 정밀도값 < 정수자리수의 경우는 %e서식자의 동작방식과 동일하다는 것을 알 수 있습니다. 그렇기 때문에 %g서식자의 정밀도값 < 정수자리수경우 정밀도 옵션을 약간 수정한 뒤 %e서식자출력코드로 보내는 방법으로 코드를 구현하면될 것 같습니다.
< %e 너비옵션 >
printf("%12e끝\n", 12.34);
printf("%13e끝\n", 12.34);
printf("%15e끝\n", 12.34);
printf("%-15e끝\n", 12.34);
1.234000e+01끝
1.234000e+01끝
1.234000e+01끝
1.234000e+01 끝
- %e서식자의 너비옵션의 경우
.
(점)과e+xx
의 자리수까지 모두 포함한 넓이 입니다.
2️⃣ %f서식자 반올림 옵션 추가
(1)단순 반올림옵션 구현
- %f서식자는 정밀도 옵션이 없을 시 기본으로 소수 여섯번째 자리까지 출력해줍니다.
- 또한 출력되는 소수점자리는 반올림이 적용된 상태로 출력 됩니다.
< 기존 ft_set_float함수 >
static void ft_set_float(double fl, char *str)
{
int cnt;
cnt = 0;
while (cnt < 16) // 십진 소수점이 최대 소수점 자리수는 16
{
fl *= 10;
str[cnt] = (int)fl + '0'; // 일의자리수를 저장
fl = fl - (int)fl;
cnt++;
}
}
<h4 align="middle" style="color:#0e435c;">< 반올림기능이 추가된 ft_set_float함수 ></h4>
```c
static void ft_set_float(t_flag *fg, double fl, char *str)
{
int cnt;
cnt = 0;
if (fg->point == 0)
fg->padding_back = 6;
ft_check_upper(&fl, fg->padding_back);
while (fg->padding_back-- > 0)
{
fl *= 10;
str[cnt] = (int)fl + '0';
fl = fl - (int)fl;
cnt++;
}
}
if (fg->point == 0)
의 조건문으로 정밀도옵션이 없을 경우 정밀도가.6
인 것 처럼 동작하도록 만들어 주었습니다.- 본격적으로 반올림을 진행해주는 함수를
ft_check_upper
함수로 만들어 주었습니다.
< ft_check_upper함수 >
static void ft_check_upper(double *fl, int cnt)
{
double upper_point;
double temp;
temp = *fl;
upper_point = 1;
while (cnt-- > 0)
{
upper_point *= 0.1;
temp = (10 * temp) - ((int)(10 * temp) / 10) * 10;
}
if ((int)temp % 10 >= 5)
*fl += upper_point;
}
if ((int)temp % 10 >= 5)
를 통해 반올림을 할 자리의 숫자를 판별하게 됩니다.%
연산자를 사용하기 위해서는temp
변수를 강제로 int자료형으로 변환 시켜줘야 합니다. 하지만if ((int)(*fl / upper_point) % 10 >= 5)
이런식으로 처리를 하게된다면 int자료형버퍼 오버플로우가 일어나 계산에 오차가 생길 것 같았습니다. 그래서while문
을 돌때마다temp
값을 잘라 내도록 하였습니다. (실험결과 한번에 처리하여 오버플로우가 일어나도 계산상의 오차가 발생하지 않았습니다.)
(2) 반올림 오류
- 최종적으로 구현한 함수를 테스트해 보았습니다.
- 대부분의 경우 기존 내장함수인
printf
함수와 동일하게 출력 되었지만 몇몇의 경우에서 다른값이 출력되었습니다. - 크게볼 때 정수부분이
1
일 경우, 소수점이5
인 경우, 소수점이 커질 때 끝자리가 반올림적용되지 않는 경우로 기존printf
함수와 차이가 생겼습니다. - 반올림을 하는 방식이 차이가 있는 것 같습니다.
printf("%.30f\n", 1.55);
ft_printf("%.30f\n", 1.55);
printf("%.2f\n", 123.55);
ft_printf("%.2f\n", 123.55);
printf("%.30f\n", 2.666666);
ft_printf("%.30f\n", 2.666666);
printf("%.30f\n", 2.666666000000000202874161914223);
ft_printf("%.30f\n", 2.666666000000000202874161914223);
1.550000000000000044408920985006
1.550000000000000000000000000000
123.55
123.54
2.666666000000000202874161914224
2.666666000000000202874161914223
2.666666000000000202874161914224
2.666666000000000202874161914223