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
>Solution :
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.