I'm wondering what the proper way is too convert a double of unknown value size to a string without allocating too much memory. Is there any way to figure out the count of digits in a double? I don't want to allocate too much or too less memory.

- 143,097
- 13
- 135
- 256

- 385
- 2
- 18
-
3With [`snprintf`](http://www.cplusplus.com/reference/cstdio/snprintf/) you can first get the length of the representation. The first 2 parameters should be `NULL` and 0 respectively. Also note that `snprintf` is found in POSIX.1-2001, POSIX.1-2008, C99, so you should compile with `-std=c99` – Pablo Jan 04 '18 at 16:56
-
How many MByte are you talking about? What do you mean with allocating? – meaning-matters Jan 04 '18 at 16:57
-
I so wish POSIX had standardized [`asprintf()`](http://man7.org/linux/man-pages/man3/asprintf.3.html), and all these nice POSIX extensions included in C11. No such luck, though. – Nominal Animal Jan 04 '18 at 18:17
3 Answers
You can use NULL
as the first argument of snprintf
to get the size:
C99 allows str to be NULL and gives the return value (as always) as the number of characters that would have been written in case the output string has been large enough.
And then malloc
:
int main(void)
{
double num = 3.14;
size_t len;
char *str;
len = (size_t)snprintf(NULL, 0, "%f", num) + 1;
str = malloc(len);
snprintf(str, len, "%f", num);
puts(str);
free(str);
return 0;
}

- 143,097
- 13
- 135
- 256

- 39,972
- 7
- 52
- 94
-
Using `"%f` with values like 1.23456e-20 result in an uninformative "0.000000" – chux - Reinstate Monica Jan 04 '18 at 18:18
Here you have a basic example of how to use snprintf
when you only want to
know the length of the representation:
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
int main(void)
{
int size = snprintf(NULL, 0, "%.15lf", M_PI);
char *pi = malloc(size + 1);
sprintf(pi, "%.15lf", M_PI);
printf("PI: '%s'\n", pi);
free(pi);
return 0;
}

- 13,271
- 4
- 39
- 59
-
1For maximum precision (if you care about precision rather than the style of output), you should change that to `int size = snprintf(NULL, 0, "%.*g", DBL_DECIMAL_DIG, M_PI); char *pi = malloc(size + 1); sprintf(pi, "%.*g", DBL_DECIMAL_DIG, M_PI);`. – Ian Abbott Jan 04 '18 at 17:20
-
@IanAbbott nice trick, I didn't know about that (to be fair I haven't read the whole `printf` man page). I didn't want to have maximum precision, I just wanted to have a very long representation. – Pablo Jan 04 '18 at 17:25
-
1@IanAbbott Minor: `DBL_DECIMAL_DIG-1` is sufficient to form the string and convert back to the same `double` with `"%.*e"`. Of course an extra digit is not bad. `"%.*g"` is always a bit trickier. `DBL_DECIMAL_DIG` may/may not be needed there. – chux - Reinstate Monica Jan 04 '18 at 18:48
Proper way to convert a double to string without allocating more memory than required?
Use snprintf()
to determine the character count need.
double value = foo();
int size = snprintf(NULL, 0, some_format, value);
if (size < 0) Handle_encoding_error(); // This is very rare
else {
char *buffer = malloc(size + 1u);
if (buffer == NULL) Handle_OOM_error(); // This is also rare
else {
snprintf(buffer, 0, some_format, value);
puts(buffer);
free(buffer);
}
}
What format to use depends on coding goals. "%f"
often results in many uninformative digits for large values and "0.00000"
for about half of all double
as many are wee values less than 0.0000005 in magnitude.
A good way to print a double
with significant digits employ exponential notation. What is great about using exponential notation is not only is the double
fully expressed, it maximum string size need can be pre-calculated (about 28) and is not excessive.
By printing with exponential notation and precision of DBL_DECIMAL_DIG - 1
, code does not need more digits to "round-trip" the string back to the same double
with strtod()
or sscanf()
.
"%a" // Hexadecimal signicand
"%.*e" // Decimal
int size = snprintf(NULL, 0, "%a", value);
// or
int size = snprintf(NULL, 0, "%.*e", DBL_DECIMAL_DIG-1, value);
// or directly with minimal extra space
// - d . dddddd e - expo \0
#define N (1 + 1 + 1 + (DBL_DECIMAL_DIG - 1) + 1 + 1 + 6 + 1)
char buf[N];
snprintf(buf, sizeof buf, "%.*e", DBL_DECIMAL_DIG-1, value);
See Printf width specifier to maintain precision of floating-point value

- 143,097
- 13
- 135
- 256