Reputation:
I have an ascii "15605632.68128593" and I wish to convert it to a double without losing accuracy
double d;
d=(double)atof("15605632.68128593");
printf("%f",d);
printed result is 15605632.681286
Any ideas?
Upvotes: 3
Views: 14866
Reputation: 154582
Goal: Convert "15605632.68128593" to a double without losing accuracy.
atof()
accomplished that to best the program could do. But since "15605632.68128593"
(a 16-digit number) is not exactly representable as a double
in your C
, it was approximated to 1.560563268128593080...e+07
. Thus accuracy was lost, albeit a small loss.
Typical double
can represent about 264 different numbers. The nearby candidates and OP's string are shown below for reference.
15605632.68128 592893... previous double
"15605632.68128 593" code's string
15605632.68128 593080... closest double
15605632.68128 6 output
The grief comes when attempting to print, thinking that what printed was the exact value of x
. Instead the nearby double
value was printed. Printout is also rounded. Using the %f
specifier defaults to 6 places to the right of the '.' giving the reported 15605632.681286
, a 14 digit number.
A better way to see all the significant digits for all double
is to use the %e
format with DBL_DIG
or DBL_DECIMAL_DIG
. DBL_DIG
is the most number of digits to the right of the '.', in decimal exponential notation %e
, to show all the digits needed to "round-trip" a double
(string to double to string without a string difference). Since %e
always shows 1 digit to the left of '.', the print below shows 1 + DBL_DIG
significant digits. DBL_DECIMAL_DIG
is 17 on my mine and many C
environments, but it vary.
If you wish to show all the significant digits, you need to qualify what is significant. The nextafter()
function shows the next representable double
. So we might want to show at least enough digits to distinguish x and the next x. I recommend DBL_DECIMAL_DIG
. Details
The exact value the program used for your "1.560563268128593e+07"
is 15605632.68128593079745769500732421875
. There are few situations where you need to see all those digits. Even is you request lots of digits, at some point, printf()
just gives you zeros.
#include <stdio.h>
#include <float.h>
#include <tgmath.h>
int main(int argc, char *argv[]) {
double x;
x = atof("15605632.68128593");
printf("%.*le\n",DBL_DIG, x); // All digits "round-trip" string-to-double-string w/o loss
printf("%.*le\n",DBL_DIG + 1, x); // All the significant digit "one-way" double-string
printf("%.*le\n",DBL_DIG + 1, nextafter(x, 2*x)); // The next representable double
printf("%.*le\n",DBL_DIG + 3, x); // What happens with a few more
printf("%.*le\n",DBL_DIG + 30, x); // What happens if you are a bit loony
return 0;
}
1.560563268128593e+07
1.5605632681285931e+07
1.5605632681285933e+07
1.560563268128593080e+07
1.560563268128593079745769500732421875000000000e+07
Upvotes: 6
Reputation: 215617
double
does not have that much precision. It can only round-trip 15 (DBL_DIG
from float.h
) decimal places from decimal string to double
back to decimal string.
Edit: While, in general, my claim is true, it doesn't seem to be your problem here. While there exist 16-decimal-place numbers which can't be round-tripped, this particular input can.
Upvotes: 1
Reputation: 47020
It's likely you're not getting all the trailing decimal places. Try printf("%.8f", d)
.
You might also try sscanf("15605632.68128593", "%lf", &d)
in place of the atof
call.
It's also not necessary to cast the result of atof
to double
. It's already a double. But the cast does no harm.
Note that - at least about 6 years ago when I looked at this in detail - many printf
and scanf
implementations were buggy in the sense that they didn't function as perfect inverses as you'd assume. Visual C/C++ and gcc both had problems in their native implementations. This paper is a useful reference.
Cygwin with gcc 4.3.4:
#include <stdio.h>
int main(void)
{
double x;
sscanf("15605632.68128593", "%lf", &x);
printf("%.8f\n", x);
return 0;
}
And then:
# gcc foo.c
# ./a
15605632.68128593
Upvotes: 7