[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;">&lt; 반올림기능이 추가된 ft_set_float함수 &gt;</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




© 2021.02. by kirim

Powered by kkrim