What is the minimum buffer size for sprintf with %g?
Question
The problem is to statically allocate a buffer large enough to fit a printed double, formatted with %g at maximum precision. This seems like a simple enough task, bu I'm having trouble. The best I have come up with (assuming the number to be printed is x) is
char buf[1 + DBL_DIG + DBL_DIG + 1 + 1 + 1 + DBL_DIG + 1];
int len = sprintf(buf, "%.*g", DBL_DIG, x);
The DBL_DIG macro is from float.h, and apparently it is supposed to indicate the maximum precision for the type double. We need:
- 1 byte for a negative sign
- enough bytes to capture the significant digits
- at most one 'separator' char (comma, etc.) per digit
- 1 byte for a decimal point
- 1 byte for 'e'
- 1 byte for the sign on the exponent
- some bytes for the exponent
- 1 byte for the trailing null written by sprintf.
I'm using the number of significant digits as an upper bound on the number of digits in the exponent. Have I made any errors? Is there a better solution? Should I just allocate 64, 128, or 256 bytes and hope for the best?
Solution 4
Instead of using sprintf
, you could use asprintf
. This allocates a buffer of the correct size to fit your string.
OTHER TIPS
You cannot pre-calculate the size at compile time. The %g formatter takes the locale into account (for the 1000's separator etc.) See http://linux.die.net/man/3/sprintf for a description on how to calculate the size safely.
Use snprintf()
to find out how many characters you need:
#include <float.h> /* DBL_DIG */
#include <stdio.h>
#include <stdlib.h>
int main(void) {
double x = rand() / (double)RAND_MAX;
char find_len[1];
int need_len;
char *buf;
need_len = snprintf(find_len, 1, "%.*g", DBL_DIG, x);
buf = malloc(need_len + 1);
if (buf) {
int used = sprintf(buf, "%.*g", DBL_DIG, x);
printf("need: %d; buf:[%s]; used:%d\n", need_len, buf, used);
free(buf);
}
return 0;
}
You need a C99 compiler for snprintf()
.
snprintf()
was defined by the C99 standard. A C89 implementation is not required to have snprintf()
defined, and if it has as an extension, it is not required to "work" as described by the C99 Standard.
Two things: %g does not show all of the representable digits, %g shows a nice-for-humans rounded result. You can specify the precision using %f or %e if you would like a different result.
Never use sprintf() rather than using snprintf(). In your case:
int len = snprintf(buf, dimensionof(buf), "%.*f", DBL_DIG, x);
Should I just round up to 64, 128, or 256 and hope for the best?
Yes, just do that -.-