I'd want to convert float/double to string, in Java and C, such that the outputs are both consistent and user friendly.
By "user friendly", I mean the string should be human readable and sound: a maximum number of significant digits, and some automatic switching to scientific notation when appropiate (the double could span all the valid range).
By "consistent" I mean that the strings should be exactly the same in Java and C (I'd tolerate some exceptions if they are really rare).
Why not use simply some printf
format string, as "%.5g"
? That works... almost. But sadly the meaning of the precision field is quite different in Java and C. Also, the switching from-to scientific notation is not very consistent, nor even the format itself (2 or 3 digits for the exponent...). And different C compilers sometimes produce different results.
Examples of differences for "%.5g"
double Java %.5g gcc %.5g tcc %.5g
1234.0 1234.0 1234 1234
123.45678 123.46 123.45678 123.46
0.000123456 0.00012346 0.00012346 0.00012346
0.000000000000123456 1.2346e-13 1.2346e-13 1.2346e-013
I can code a function in C or Java (or both), but I wonder if someone has already dealt with this. I'm not very concerned with performance, but yes with portability across C compilers.
If you really want base-10 floating-point output, it's probably easiest to write a JNI wrapper for C's printf
here. The Java folks decided they needed to do printf
themselves. Apart from what you've already noticed about %g
, they decided to change the rounding behaviour and truncate output in a curious way. To wit:
System.out.printf("%.5g\n", 1.03125);
System.out.printf("%.5g\n", 1.09375);
1.0313
1.0938
gcc
correctly rounds to even:
printf("%.5g\n", 1.03125);
printf("%.5g\n", 1.09375);
1.0312
1.0938
Notice that 1.03125 and 1.09375 are exactly representable as doubles since 1/32 = 0.3125.
Java's printf %g format wrongly truncates its output:
double d = 1;
for (int i = 0; i < 1035; i++) d /= 2;
System.out.printf("%.20g\n%.20a\n", d, d);
2.7161546124360000000e-312
0x0.00080000000000000000p-1022
Here's the right answer:
double d = 1;
for (int i = 0; i < 1035; i++) d /= 2;
printf("%.20g\n%.20a\n", d, d);
2.7161546124355485633e-312
0x0.00080000000000000000p-1022
1.0e-200
is normal but not exactly representable. Java pretends not to notice:
System.out.printf("%.20g\n%.20a\n", 1.0e-200, 1.0e-200);
1.0000000000000000000e-200
0x1.87e92154ef7ac0000000p-665
Here's the right answer:
printf("%.20g\n%.20a\n", 1.0e-200, 1.0e-200);
9.999999999999999821e-201
0x1.87e92154ef7ac0000000p-665
So you've either got to live with bizarro rounding behaviour in your printf or you piggyback off gcc
and glibc
's work. I can't recommend trying to print out floating-point numbers by yourself. Or you can just use %a
, which AFAIK works perfectly fine in Java.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With