QuesterDesura
QuesterDesura

Reputation: 415

Proper way to convert a double to string without allocating more memory than required?

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.

Upvotes: 1

Views: 1353

Answers (3)

chux
chux

Reputation: 154245

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

Upvotes: 1

David Ranieri
David Ranieri

Reputation: 41036

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;
}

Upvotes: 6

Pablo
Pablo

Reputation: 13590

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;
}

Upvotes: 5

Related Questions