4

In order to convert a double (or float, int, long) to char* in C we can use

i) sprintf()

ii) functions like itoa(), ftoa()

But all these functions expect a buffer as a parameter. What should be size of the buffer in each case so that the maximum integer or the maximum precision float/double can be converted ?

Thomas Weller
  • 55,411
  • 20
  • 125
  • 222
user1423561
  • 327
  • 1
  • 3
  • 18
  • 1
    http://stackoverflow.com/questions/3068397/finding-the-length-of-an-integer-in-c See if this helps. Don't know about floats however. – rubikonx9 Mar 19 '15 at 10:18
  • There is no standard `ftoa` function. –  Mar 19 '15 at 11:10

4 Answers4

3

snprintf can be used to determine the size, with a 0 argument as the size:

double x = 42;
const char fmt[] = "%f";
int size = snprintf(0, 0, fmt, x);
if(size < 0) { /* handle error */ }
char buf[size+1];
sprintf(buf, fmt, x);

The C standard requires snprintf with a 0 size argument to work. Note, though, that some older implementations don't conform (e.g. Glibc until version 2.0.6 returns -1 if the output was truncated instead of the number of characters that would have been written, according to the man page).

The error checking above may be useless, the only possible error which can occur is an encoding error.

You shouldn't change the locale between the snprintf and the sprintf call, the locale may affect the size. The sprintf can be replaced by a snprintf call again if intervening locale changes are a concern, but then you probably want to allocate the memory via malloc, and then realloc while necessary. [Thanks, Chux!]

Under Glibc, there is also asprintf which allocates the buffer itself. Don't forget to free it afterwards. [Thanks to Abligh!]

Community
  • 1
  • 1
mafso
  • 5,433
  • 2
  • 19
  • 40
  • extremely pedantic point: C11 doesn't require this to work as VLAs are optional . – M.M Mar 19 '15 at 14:06
  • Concerning `locale` change (or if x may be `volatile`), suggest calling `snprintf()` the 2nd time too. – chux - Reinstate Monica Mar 19 '15 at 14:22
  • @MattMcNabb: Yes, I've used them here because it's one of the few cases where I find VLAs (not pointers to VLAs) useful: You haven't got a good upper bound for the length but you know it won't be "huge". It's basic C knowledge how to do it without them, and not really related to the question, so I didn't mention it. – mafso Mar 19 '15 at 16:30
  • @chux: And then do what? In a debug built, you could `abort`. Either way, I think it's best to document the function to not accept locale changes during its execution. Alternatively, `malloc` and `realloc` in a loop until everything worked. But yes, probably good to mention, I'll edit later, I don't have the time currently. – mafso Mar 19 '15 at 16:36
  • 1
    Under `glibc` you can use `asprintf` to allocate a buffer of the correct size. – abligh Mar 19 '15 at 17:22
  • I think asprintf is the right choice. Memory allocation is taken care of. – user1423561 Mar 20 '15 at 05:39
1

Make the buffer just large enough. A double has about 15 places plus exponent and signs. A buffer char buffer[25] should do.

DrKoch
  • 9,556
  • 2
  • 34
  • 43
  • And what exactly is "large enough"? This depends on architecture etc. Also, it'll never be minimum possible value (if it matters). – rubikonx9 Mar 19 '15 at 10:21
  • As long as the architecture uses IEEE floating point the max _length_ of a double in string representation should be rather constant. Just print pi * -1.0 * 1e-15 as a string and count characters, add one for security and one for `\0` – DrKoch Mar 19 '15 at 10:24
  • 1
    It's really bad advice to do size calculations that sloppily. `%f`, for example, prints 6 digits after the `.` by default, what makes `char[25]` too small for some IEEE double values. – mafso Mar 19 '15 at 11:24
  • @mafso Sadly, true. So my answer isn't helpful in the general case. – DrKoch Mar 19 '15 at 11:29
  • If you get a nice formula for the size akin to `sizeof(int) * CHAR_BIT / 3 + 2` for `int`, I'll consider this a very helpful answer. But I don't know of a somewhat reasonable way to get an upper bound, especially because I don't know if the length of the decimal point has an upper bound across all locales (maybe `MB_LEN_MAX`?). `DECIMAL_DIG` from `` (and other macros from there) might be helpful. – mafso Mar 19 '15 at 11:43
  • this is exactly the kind of thinking that leads to exploitable software (unless you also use `snprintf` to print, that is) – M.M Mar 19 '15 at 14:07
  • @mafso To uniquely print every `double` in exponential notation, code needs to print at least `DBL_DECIMAL_DIG` digits. `DBL_DECIMAL_DIG` is in ``. [Ref](http://stackoverflow.com/questions/16839658/printf-width-specifier-to-maintain-precision-of-floating-point-value) – chux - Reinstate Monica Mar 19 '15 at 14:17
1

Unfortunately, there is no way to limit the length with the %f specifier, as the length parameter is a minimum, not a maximum. So large values take as many characters as their exponent, not counting the decimals.

For example, "%f.10", 1.e20f yields 100000002004087730000.0000000000.

In this respect, sprintf is extremely unsafe (should be banned), prefer snprintf.

1

To convert a double to a string and using sprintf(), the buffer size needs depends on the format specifier.

Note that the buffers below are fixed at compile time, not VLA.

//       -   digits               .   000000 \0
char buf[1 + 1 + DBL_MAX_10_EXP + 1 + 6 + 1];
sprintf(buf, "%f", x);

//       -   digit.digits          e   -  xxxx \0
char buf[1 + 2 + DBL_DECIMAL_DIG + 1 + 1 + 4 + 1];
sprintf(buf, "%.*e", DBL_DECIMAL_DIG, x);

// Others: %0.100f %a %*.*E etc.

But printf() functions consider their locale so additional characters could show up. Suggest oversizing buffer to 2x the expected size, using snprintf() and then sleeping well at night.

//       -   digit.digits          e   -  xxxx \0
char buf[(1 + 2 + DBL_DECIMAL_DIG + 1 + 1 + 4 + 1)*2];
int len = snprintf(buf, "%.*e", DBL_DECIMAL_DIG, x);
if (len < 0 || len >= sizeof buf) {
  Handle_Insanity();
}

Ref: Printf width specifier to maintain precision of floating-point value

Community
  • 1
  • 1
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256