When R converts a large number to a string in scientific notation, it includes all significant digits and no trailing zero's. Is it possible to accomplish this in C with sprintf
?
> as.character(12345e11)
[1] "1.2345e+15"
> as.character(1234500000e6)
[1] "1.2345e+15"
> as.character(1234500001e6)
[1] "1.234500001e+15"
I tried sprintf(buf, "%g", val)
but this seems to include 5 decimal digits at most. I also tried to set a higher precision with sprintf(buf, "%.18g", val)
but this will include non significant digits and trailing zeros.
Is there a way to get the behavior of sprintf(buf, "%g", val)
but increase the 5 digit limit?
%g and number of significant digits When using %g, the precision determines the number of significant digits. The default precision is 6.
We round a number to three significant figures in the same way that we would round to three decimal places. We count from the first non-zero digit for three digits. We then round the last digit. We fill in any remaining places to the right of the decimal point with zeros.
Zeros between non zero digits are significant. Zeros to the left of the first non zero digit are not significant. more easily seen if it is written as 3.4x10-5) ~ 0.001111 has four significant figures. Trailing zeros (the right most zeros) are significant when there is a decimal point in the number.
Code could use "%.18e"
or "%.18g"
, but the question is how large should "18" be? Is 18 the best value? The answer lies in DBL_DECIMAL_DIG
.
DBL_DECIMAL_DIG
is the minimum number of significant digits to print to insure the round-trip of double
to string to the same exact double
for all double
.
Recommend using format specifier "%.*e"
.
Note that the "18" in "%.18e"
is the number of significant digits after the decimal point. So "%.18e"
prints 19 significant digits.
Use printf("%a", x);
which prints in a hexadecimal output.
For a decimal output:
#include <float.h>
// sign + digit + dp + digits + e + sign + expo + \0
char buf[1 + 1 + 1 + (DBL_DECIMAL_DIG - 1) + 1 + 1 + 5 + 1];
sprintf(buf, "%.*e", DBL_DECIMAL_DIG - 1, x);
Ref Printf width specificer to maintain precision of floating-point value
A number like y = 1.0/3.0
using the typical double
binary64 format would need to see about 53 decimal digits to see its exact value. But many of the trailing digits are not needed for a successful round-trip.
Now we know the most digits to print, use the below to get rid of those pesky trailing 0
digits.
#include <float.h>
#include <math.h>
#include <stdio.h>
#include <string.h>
char *trim0(double x, char *buf) {
sprintf(buf, "% .*e", DBL_DECIMAL_DIG - 1, x);
if (isfinite(x)) {
char *p = &buf[DBL_DECIMAL_DIG + 1]; // address of last significand digit
char *t;
for (t=p; *t == '0'; t--);
memmove(t+1, p+1, strlen(p+1)+1);
}
return buf;
}
int main(void) {
char buf[1 + 1 + 1 + (DBL_DECIMAL_DIG - 1) + 1 + 1 + 5 + 1];
printf("%s\n", trim0(1.2, buf));
printf("%s\n", trim0(1.0/7, buf));
return 0;
}
Output
1.2e+00
1.4285714285714285e-01
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