Home > Software engineering >  printf - implementation in C
printf - implementation in C

Time:03-24

This may be a silly question, but... I tried to implement printf, but for some reason the output I get is not exactly what I expected. any idea what it could be? I would appreciate some help.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdarg.h> 

static int print(const char *restrict fmt, ...);
static int getfloat(float *);
static char *itoa(int, char *, int);
static void _strrev(char *);


int
main(void)
{
    float i1 = 0.0, i2 = 0.0, noi1 = 0.0, noi2 = 0.0, res = 0.0;

    print("weight - item 1: ");
    getfloat(&i1);

    print("no. of item 1: ");
    getfloat(&noi1);

    print("weight - item 2: ");
    getfloat(&i2);

    print("no. of item 2: ");
    getfloat(&noi2);

    res = ((i1 * noi1)   (i2 * noi2)) / (noi1   noi2);
    print("%f\n", res);

    exit(EXIT_SUCCESS);
}


static int
print(const char *restrict fmt, ...)
{
    va_list ap;
    char buf[BUFSIZ] = {0}, tmp[20] = {0};
    char *str_arg;
    int i = 0, j = 0;

    va_start(ap, fmt);

    while (fmt[i] != '\0') {

        if (fmt[i] == '%') {
            i  ;
            switch (fmt[i]) {
                case 'c':
                    buf[j] = (char)va_arg(ap, int);
                    j  ;
                    break;

                case 'd':
                    itoa(va_arg(ap, int), tmp, 10);
                    strcpy(&buf[j], tmp);
                    j  = strlen(tmp);
                    break;

                case 'f':
                    gcvt(va_arg(ap, double), 10, tmp);
                    strcpy(&buf[j], tmp);
                    j  = strlen(tmp);
                    break;

                case 's':
                    str_arg = va_arg(ap, char *);
                    strcpy(&buf[j], str_arg);
                    j  = strlen(str_arg);
                    break;

                default:
                    break;
            }
        } else { buf[j   ] = fmt[i]; } 
          i;
    }
    fwrite(buf, j, 1, stdout);
    va_end(ap);
    return (j);
}


static int
getfloat(float *p)
{
    int c, sign = 0;
    float pwr = 0.0;


    while (c = getc(stdin), c == ' ' || c == '\t' || c == '\n')
        ;   /* ignore white spaces */
    
    sign = 1;   /* record sign */
    if (c == ' ' || c == '-') {
        sign = (c == ' ') ? 1 : -1;
        c = getc(stdin);
    }


    for (*p = 0.0; isdigit(c); c = getc(stdin))
        *p = 10.0 * *p   c - '0';

    if (c == '.') { c = getc(stdin); }

    for (pwr = 1.0; isdigit(c); c = getc(stdin)) {
        *p = 10.0 * *p   c - '0';
        pwr *= 10.0;
    }
    
    *p *= sign / pwr;
    if (c != EOF)
        ungetc(c, stdout);
    return (float)c;
}


static char *
itoa(int n, char *strout, int base)
{
    int i, sign;

    if ((sign = n) < 0)
        n -= n;

    i = 0;
    do {
        strout[i  ] = n % base   '0';
    } while ((n /= base) != 0);

    if (sign < 0) { strout[i  ] = '-'; }
    strout[i] = '\0';

    _strrev(strout);
    return (strout);
}


static void
_strrev(char *str)
{
    int i = 0, j = strlen(str) - 1;

    for ( ; i < j;   i, --j) {
        int tmp = str[i];
        str[i] = str[j];
        str[j] = tmp;
    }
}

here is the output I get: 19.44444466

and this is the output that I expect: (or the one that I would at least like to receive, which is the one in itself that I get when I use printf)

19.444445 

CodePudding user response:

   f, F   The double argument is rounded and converted to decimal
          notation in the style [-]ddd.ddd, where the number of
          digits after the decimal-point character is equal to the
          precision specification.  If the precision is missing, it
          is taken as 6;

https://man7.org/linux/man-pages/man3/printf.3.html

The default precision for %f is six so printf() is rounding the result to six decimal places.

You'd need to play with the ndigit argument to gcvt() which is the total number of significant digits (both before and after the decimal point). You are passing in 10 so your answer has two digits before the decimal and eight after for this particular number.

CodePudding user response:

Seems that gcvt() don't do exactly what printf() do, at least with your compiler. Check it with a "real" printf with the same value.

Since you didn't gave the numbers you used for the test (avoid getfloat() and initialize directly i1, i2, noi1 and noi2 with required constants in your question), I can't run it and tell you why exactly - or if it even happens with my own compiler.

Usually, the source code for printf is at least two times bigger than yours, so you may have missed some vicious subcases. If I remember well, printf has code to decode an IEEE-754 directly and don't rely on gcvt.

  •  Tags:  
  • c
  • Related