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
.