I am trying to implement a simple version of a function similar to dtoa but without arbitrary precision. I name it ftob which also handles arithmetic bases other than 10 (2-36). It works with long double, which on my machine is : x86 extended pricision
The function works fine, but on certain values it gives erroneous and unacceptable results, e.g. 2.5600 ,
here is my code:
#include<stdio.h>
#include<math.h>
char *ftob(long double ld, char *str, unsigned short int n_digits, unsigned short int base)
{
long double ftob_tmp;
short unsigned index = 1;
short const sign = (ld < 0.0L) ? -1 : 1;
short int i = 0, j = 0 , k = 0;
//check base, number.
if(base < 2 || base > 36)
return NULL;
else if(ld == INFINITY) {
str = "inf";
return str;
}
else if(ld == -INFINITY) {
str = "-inf";
return str;
}
else if(__isnanl(ld)) {
str = "nan";
return str;
}
//initialisations
(sign == -1) ? str[i++] = '-' : 0;
ftob_tmp = sign * ld;
while(ftob_tmp > 0.0L && ftob_tmp < 1.0L) {
ftob_tmp *= base;
j++;
}
while(ftob_tmp >= base) {
ftob_tmp /= base;
j--;
}
//reinitialise
ftob_tmp = sign * ld;
if(ftob_tmp >= 0.0L && ftob_tmp < 1.0L) {
str[i++] = '0';
str[i++] = '.';
for(k = 0; k < j - 1 && k < n_digits - 1; k++)
str[i++] = '0';
n_digits -= j;
}
else if(ftob_tmp >= base)
k = i - j + 1;
else
k = i + 1;
ftob_tmp *= powl(base, --j);
// printf("%0.20Lf\n", ftob_tmp); /*debug message*/
//main loop
for(n_digits += i; i < n_digits; i++) {
if(i == k)
str[n_digits++, i] = '.';
else {
// printf("%0.20Lf * %Lf = %0.20Lf\n", ftob_tmp, powl(base, index), ftob_tmp * powl(base, index)); /* debug message*/
str[i] = (int)fmodl((ftob_tmp * powl(base, index++)), base);
str[i] += (str[i] < 10) ? '0' : 'A' - 10;
}
}
//finalise
str[i] = '\0';
return str;
}
int main(void)
{
char ftl[300];
printf("ftl = \"%s\"\n", ftob(2.56L, ftl, 19, 10));
return 0;
}
the output for ftob(2.56L, ftl, 19, 10)
is:
ftl = "2.550990990990990990"
Uncommenting the debug message gives:
0.25599999999999999999
0.25599999999999999999 * 10.000000 = 2.55999999999999999995
0.25599999999999999999 * 100.000000 = 25.59999999999999999861
0.25599999999999999999 * 1000.000000 = 255.99999999999999998612
0.25599999999999999999 * 10000.000000 = 2560.00000000000000000000
0.25599999999999999999 * 100000.000000 = 25599.99999999999999822364
0.25599999999999999999 * 1000000.000000 = 255999.99999999999998578915
0.25599999999999999999 * 10000000.000000 = 2560000.00000000000000000000
0.25599999999999999999 * 100000000.000000 = 25599999.99999999999818101060
0.25599999999999999999 * 1000000000.000000 = 255999999.99999999998544808477
0.25599999999999999999 * 10000000000.000000 = 2560000000.00000000000000000000
0.25599999999999999999 * 100000000000.000000 = 25599999999.99999999813735485077
0.25599999999999999999 * 1000000000000.000000 = 255999999999.99999998509883880615
0.25599999999999999999 * 10000000000000.000000 = 2560000000000.00000000000000000000
0.25599999999999999999 * 100000000000000.000000 = 25599999999999.99999809265136718750
0.25599999999999999999 * 1000000000000000.000000 = 255999999999999.99998474121093750000
0.25599999999999999999 * 10000000000000000.000000 = 2560000000000000.00000000000000000000
0.25599999999999999999 * 100000000000000000.000000 = 25599999999999999.99804687500000000000
0.25599999999999999999 * 1000000000000000000.000000 = 255999999999999999.98437500000000000000
0.25599999999999999999 * 10000000000000000000.000000 = 2560000000000000000.00000000000000000000
ftl = "2.550990990990990990"
The source of the error seems to be that 0.256 cannot be represented exactly in long double and has a value about 0.255999999999999999989374818710
.
But i am okay if I get output:
flt = "2.5599999999999999999"
instead of :
flt = "2.5600000000000000000"
The problem is that in the "main loop" in the fourth round it is arbitrarily rounded to 2560.00000
which causes str[i] to be set to 0 instead of 9. This is also because 2559.99999999999999...
cannot be represented in long double.
But I only need '2559' to be representable so str[i] can be set to 9. (and so for every round in the loop).
I request advice on how I can achieve this, or if it is achievable at all.
Thanks in Advance,