71

When I convert an unsigned 8-bit int to string then I know the result will always be at most 3 chars (for 255) and for an signed 8-bit int we need 4 chars for e.g. "-128".

Now what I'm actually wondering is the same thing for floating-point values. What is the maximum number of chars required to represent any "double" or "float" value as a string?

Assume a regular C/C++ double (IEEE 754) and normal decimal expansion (i.e. no %e printf-formatting).

I'm not even sure if the really small number (i.e. 0.234234) will be longer than the really huge numbers (doubles representing integers)?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
martin
  • 1,106
  • 2
  • 9
  • 11
  • 7
    Jalf, why would anybody mention that? Who said he's asking how big a fixed-size buffer would need to be? Maybe he wants to know how many character columns he needs to reserve on the console for a text-based table. – Rob Kennedy Nov 09 '09 at 19:27
  • Without scientific notation it would be long for values at the extremes of the magnitude range, but what would be the point? Who would read such a number - a double (typically) has approximately 15 significant decimal digits - all the rest would be a large number of leading or traiining zeros. – Clifford Nov 09 '09 at 21:09
  • 1
    No you can have _much_ more than 15 significant digits for _decimal digits_ but only 15 significant digits for integer. This is because while you can represent all integers you can't represent all decimal expansions so fewer bits can be used to cover a larger range. – martin Nov 10 '09 at 14:39
  • I'm not printing numbers for people to read, I'm trying to find the required char buffer size needed in order to be sure that the reverse of strtod (i.e "dtoa(double d, char* output)") can finish safely with no risk of buffer overflows. – martin Nov 10 '09 at 14:41
  • @matrin, I tried with for loop and multiplied the number as long as it gave `1.#INF00`, largest number was 286 bytes long. So i guess you are safe with 512 bytes? (using printf). – Rookie Apr 07 '13 at 08:52
  • @jalf The problem is that I know no valid way to format `std::string` using standard `printf` format. So if I get a double, I need to create `char[X]` buffer. – Tomáš Zato Dec 07 '15 at 10:41

12 Answers12

42

The standard header <float.h> in C, or <cfloat> in C++, contains several constants to do with the range and other metrics of the floating point types. One of these is DBL_MAX_10_EXP, the largest power-of-10 exponent needed to represent all double values. Since 1eN needs N+1 digits to represent, and there might be a negative sign as well, then the answer is

int max_digits = DBL_MAX_10_EXP + 2;

This assumes that the exponent is larger than the number of digits needed to represent the largest possible mantissa value; otherwise, there will also be a decimal point followed by more digits.

CORRECTION

The longest number is actually the smallest representable negative number: it needs enough digits to cover both the exponent and the mantissa. This value is -pow(2, DBL_MIN_EXP - DBL_MANT_DIG), where DBL_MIN_EXP is negative. It's fairly easy to see (and prove by induction) that -pow(2,-N) needs 3+N characters for a non-scientific decimal representation ("-0.", followed by N digits). So the answer is

int max_digits = 3 + DBL_MANT_DIG - DBL_MIN_EXP

For a 64-bit IEEE double, we have

DBL_MANT_DIG = 53
DBL_MIN_EXP = -1023
max_digits = 3 + 53 - (-1023) = 1079
Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
  • 2
    Sorry, this is wrong - the longest numbers will be very small ones, not very large ones, and it's more complicated to work out their length. It should be possible to work it out from DBL_MIN_EXP and DBL_MANT_DIG; I'll update the answer if I can work it out. – Mike Seymour Nov 09 '09 at 18:47
  • 1
    Thanks for the great answer Mike. In case someone needs clarification: Basically, floating-point has a special rule that says if all bits in the exponent are zero then the number is called "denormalized" and the implicit leading "1." (which is otherwise always prefixed onto the fractional part) is not included. This means that by setting the exponent bits to all zero you can use the fractional part to get another 15-16 digits onto the length of the base10 decimal expansion (i.e. like 0.000...0001*2^-1023 where -1023 is the exponent which is encoded as all zeros due to the exponent bias). – martin Nov 10 '09 at 10:20
  • The fractional part (for the denormalized case) has an implicit "0." so you can still only count 52 decimals digits for that and not 53. Thus the answer is 1023 + 52 + 3 = 1078 digits. I wonder why Java comes up with 1077 in Fred's example below though? – martin Nov 10 '09 at 10:37
  • Just adding for future reference: by the same calculations a single float is: 3 + 24 + 127 = 154 (or 153 if martin is correct) – HairyFotr May 10 '13 at 11:59
  • 5
    -1023 as the DBL_MIN_EXP is bits and not digits, so the calculation of max_digits here does not seem right – Soren Jul 16 '14 at 14:20
  • @Soren: As I said, "It's fairly easy to see (and prove by induction) that `-pow(2,-N)` needs `3+N` characters for a non-scientific decimal representation (`"-0."`, followed by `N` digits)." `N=1` gives `-0.5` (1 fractional digit); `N=2` gives `-0.25` (2 digits); and so on, increasing by one each time. – Mike Seymour Jul 16 '14 at 14:27
  • 7
    You must be kidding me... 1079 digits? Could you make an example C program that generates that number in stdout? – Tomáš Zato Dec 07 '15 at 10:39
  • 3
    This answer is (still) WRONG. The maximum number of characters that will be required to print any decimal `double` value (i.e. in `"%f"` format) will be for the value of `-DBL_MIN` (i.e. -0x1p-1022, assuming binary64 IEEE 754 is your `double`). For that you'll need exactly 325 characters. That's: `DBL_DIG + abs(DBL_MIN_10_EXP) + strlen("-0.")`. This is of course because `log10(fabs(DBL_MIN))` is 308, which is also `abs(DBL_MIN_10_EXP)+1` (the +1 is because of the leading digit to the left of the decimal place), and that's the number of leading zeros to the left of the significant digits. – Greg A. Woods Feb 15 '18 at 01:48
  • 2
    `printf("%0.320lf\n", -DBL_MIN);` – David Schwartz Feb 22 '18 at 23:34
  • `#include ` `#include ` `int main() { printf("%0.320lf\n", -DBL_MIN); }` prints 323 characters – Boris Verkhovskiy Jun 04 '23 at 13:03
  • The answer represents floats as a multiplication of two binary numbers (without any exponentiation), something like this: `-1.1010101010101010101010101010101010101010101010101010*10000...(1015 more 0's)...0000`. `e` would be the wrong letter because it [means](https://en.wikipedia.org/wiki/Scientific_notation#E_notation) "10 to the power of the following decimal number", not "multiplied by this binary number". It is very unlikely you're using this representation. – Boris Verkhovskiy Jun 04 '23 at 13:39
26

According to IEEE 754-1985, the longest notation for value represented by double type, i.e.:

-2.2250738585072020E-308

has 24 chars.

Francisco
  • 10,918
  • 6
  • 34
  • 45
Vitaliy Ulantikov
  • 10,157
  • 3
  • 61
  • 54
  • 4
    Cool, can you explain why this is the longest one? Why can't for example a really small 0.033211233457645...234234 become longer? – martin Nov 09 '09 at 13:59
  • 2
    If you don't want the scientific notation that would give you 308 more chars then... – Vilx- Nov 09 '09 at 14:02
  • 2
    Becouse mantissa of double, following IEEE 754-1985, can represent numbers with maximum accuracy of 17 digits after point. Add to it both minuses for mantissa and period, point, e-char and 3 digits of period (8 bit), and you will get exact 24 chars. – Vitaliy Ulantikov Nov 09 '09 at 14:04
  • 3
    `0.00000000000000000 ... approx 308 ... 00002225073858507202` ==> approx 326 chars :) – pmg Nov 09 '09 at 16:04
  • @VitaliyUlantikov Hi, could you tell me why IEEE 754-1985, can represent numbers with maximum accuracy of 17 digits after point. Why 17 digits? Thanks. – user1024 Apr 11 '17 at 01:18
  • @user1024 Its 16 actually. From wiki: The number of decimal digits precision is calculated via number_of_mantissa_bits * Log10(2). Thus ~7.2 and ~15.9 for single and double precision respectively. – Vitaliy Ulantikov Apr 11 '17 at 09:12
  • @VitaliyUlantikov Hi, thanks for you reply. Is that to say: we have 53bits in double's mantissa, so the max decimal can be represented is 2^53=9007199254740992 which has 16 digits? So the max accuracy is 16 digits. – user1024 Apr 11 '17 at 12:55
  • @user1024 based on how decimals are represented in binary, you're looking for 1/2^53 ~ 1e-16, which translates to 16 digits after decimal point. – Vitaliy Ulantikov Apr 11 '17 at 15:01
  • @fortran make sure to check the question edit history before rising this. The question was changed after this answer was posted. – Vitaliy Ulantikov Jun 15 '20 at 09:01
  • btw: the smallest normalized value is [2.22507385850720**14**e-308](http://www.binaryconvert.com/result_double.html?hexadecimal=0010000000000000) – MattTT Jul 11 '23 at 15:22
13

A correct source of information that goes into more detail than the IEEE-754 Specification are these lecture notes from UC Berkely on page 4, plus a little bit of DIY calculations. These lecture slides are also good for engineering students.

Recommended Buffer Sizes

Single Double Extended Quad
16 24 30 45

These numbers are based on the following calculations:

Maximum Decimal Count of the Integral Portion

Single Double Extended Quad
9 17 21 36

(Quantities listed in decimals.)

Decimal counts are based on the formula: At most Ceiling(1 + NLog_10(2)) decimals, where N is the number of bits in the integral portion*.

Maximum Exponent Lengths

Single Double Extended Quad
5 5 7 7

(Standard format is e-123.)

Fastest Algorithm

The fastest algorithm for printing floating-point numbers is the Grisu2 algorithm detailed in the research paper Printing Floating-point Numbers Quickly and Accurately. The best benchmark I could find can be found here.

Boris Verkhovskiy
  • 14,854
  • 11
  • 100
  • 103
5

You can use snprintf() to check how many chars you need. snprintf() returns the number of chars needed to print whatever is passed to it.

/* NOT TESTED */
#include <stdio.h>
#include <stdlib.h>
int main(void) {
    char dummy[1];
    double value = 42.000042; /* or anything else */
    int siz;
    char *representation;
    siz = snprintf(dummy, sizeof dummy, "%f", value);
    printf("exact length needed to represent 'value' "
           "(without the '\\0' terminator) is %d.\n", siz);
    representation = malloc(siz + 1);
    if (representation) {
        sprintf(representation, "%f", value);
        /* use `representation` */
        free(representation);
    } else {
        /* no memory */
    }
    return 0;
}

Note: snprintf() is a C99 function. If a C89 compiler provides it as an extension, it may not do what the above program expects.

Edit: Changed the link to snprintf() to one that actually describes the functionality imposed by the C99 Standard; the description in the original link is wrong.
2013: Changed the link back to POSIX site which I prefer over the site of the first edit.

pmg
  • 106,608
  • 13
  • 126
  • 198
  • 1
    Now to answer the question one just need to figure out which number to assign to `double value = ??;` – DrBeco Jul 01 '19 at 23:25
1

Depends on what you mean by "represent". Decimal fraction don't have exact floating-point representations. When you convert decimal fraction -> binary fraction -> decimal, you do not have exact decimal representations and will have noise bits at the end of the binary representation.

The question didn't involve starting from decimal, but all source code (and must user input) is decimal, and involves the possible truncation issue. What does "exact" mean under these circumstances?

Basically, it depends on your floating point representation.

If you have 48 bits of mantissa, this takes about 16 decimal digits. The exponent might be the remaining 14 bits (about 5 decimal digits).

The rule of thumb is that the number of bits is about 3x the number of decimal digits.

S.Lott
  • 384,516
  • 81
  • 508
  • 779
  • 5
    Actually they can't have infinite digits because any binary fraction can be expressed precisely in decimal notation. – Vilx- Nov 09 '09 at 13:52
  • I mean represent as an exact base 10 decimal expansion (e.g. no %e stuff). And I'm assuming we're dealing with standard C/C++ double using IEEE 754. – martin Nov 09 '09 at 13:53
  • Of course. I mean, in the memory the double is stored in a binary notation like 1101010.101000110101. The number of digits after the binary point is pretty much finite and thus can be represented precisely in decimal. 1/2 is represented as 0.5; 1/4 is represented as 0.25; 1/8 is represented as 0.125; etc. Am I missing something? – Vilx- Nov 09 '09 at 13:59
  • The problem is the other way around. Every decimal fraction does not have an exact binary representation. So, if your "original" number was a decimal number, the float->decimal will be wrong to start with. The number of decimal digits doesn't matter if what you're comparing with was decimal. – S.Lott Nov 09 '09 at 14:01
  • 4
    Of course decimal fractions cannot be represented precisely in binary fractions. I never said otherwise. But binary fractions can be represented precisely in decimal fractions, so IMHO "Floating-point values do not have exact decimal representations and can have an infinite number of repeating decimal digits." isn't correct. – Vilx- Nov 09 '09 at 14:05
  • @Vilx: I beg to differ. I would have put it "Of course not all decimal fractions can be represented precisely in binary fractions." – Olof Forshell Jan 14 '19 at 22:47
1

You can control the number of digits in the string representation when you convert the float/double to a string by setting the precision. The maximum number of digits would then be equal to the string representation of std::numeric_limits<double>::max() at the precision you specify.

#include <iostream>
#include <limits>
#include <sstream>
#include <iomanip>

int main()
{
 double x = std::numeric_limits<double>::max();

 std::stringstream ss;
 ss << std::setprecision(10) << std::fixed << x;

 std::string double_as_string = ss.str();
 std::cout << double_as_string.length() << std::endl;
}

So, the largest number of digits in a double with a precision of 10 is 320 digits.

Charles Salvia
  • 52,325
  • 13
  • 128
  • 140
1

1024 is not enough, the smallest negative double value has 1077 decimal digits. Here is some Java code.

double x = Double.longBitsToDouble(0x8000000000000001L);
BigDecimal bd = new BigDecimal(x);
String s = bd.toPlainString();
System.out.println(s.length());
System.out.println(s);

Here is the output of the program.

1077
-0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004940656458412465441765687928682213723650598026143247644255856825006755072702087518652998363616359923797965646954457177309266567103559397963987747960107818781263007131903114045278458171678489821036887186360569987307230500063874091535649843873124733972731696151400317153853980741262385655911710266585566867681870395603106249319452715914924553293054565444011274801297099995419319894090804165633245247571478690147267801593552386115501348035264934720193790268107107491703332226844753335720832431936092382893458368060106011506169809753078342277318329247904982524730776375927247874656084778203734469699533647017972677717585125660551199131504891101451037862738167250955837389733598993664809941164205702637090279242767544565229087538682506419718265533447265625
Fred
  • 152
  • 1
  • 2
  • 1
    // prints one longer than 1077: import java.math.BigDecimal; public class Program { public static void print_bigdecimal(String name, BigDecimal bd) { String s = bd.toPlainString(); System.out.println ("NUM " + name + ": " + s + " (" + s.length() + " chars)"); } public static void main(String[] args) { print_bigdecimal("-2^-1075==", new BigDecimal(-1).divide (new BigDecimal(2).pow(1075))); print_bigdecimal("0x80...01L", new BigDecimal(Double.longBitsToDouble(0x8000000000000001L))); } } – martin Nov 10 '09 at 14:34
  • 2^-1075 is valid because it's 2^-52 + 2^-1023 – martin Nov 10 '09 at 14:36
  • What you are seeing is a bad printing algorithm. One would use the Grisu3 algorithm for this edge case. –  Aug 27 '18 at 18:41
  • @user2356685: "Edge case"? The (albeit) very small value is a valid "small value" - nothing more, nothing less, The tempreal 10-byte real type allows even smaller values. The gmplib libraries smaller still. – Olof Forshell Jan 14 '19 at 22:43
  • https://float.exposed/0x8000000000000001 – Boris Verkhovskiy Jun 04 '23 at 13:54
1

"What is the maximum length in chars needed to represent any double value?"

The exact answer to this question is: 8 ASCII chars - in a hexadicimal format, excluding the '0x' prefix - 100% accuracy :) (but it's not just a joke)

The usable precision of IEEE-754 double is around 16 decimal digits - so excluding educational purposes, representations longer than that are just a waste of resources and computing power:

  • Users are not getting more informed when they see a 700-digit-number on the screeen.

  • Configuration variables stored in that "more accurate" form are useless - every single operation on such number will destroy the accuracy. (excluding changing the sign bit)

If someone needs better real precision, then there's 80-bit long double with around 18-digit accuracy or f.e. libquadmath.

Regards.

vtomazzi
  • 19
  • 1
1

As an improvement on the accepted answer based on Greg A. Woods accurate comment, a more conservative but still adequate number of characters needed is 3 + DBL_DIG + -DBL_MIN_10_EXP (total 325) with 3 being for the leading "-0." that may be needed. If using C-style strings, add one for null ('\0') termination such that an adequately sized buffer (of size 326) may be created with:

#include <limits.h>

char buffer[4 + DBL_DIG + -DBL_MIN_10_EXP];

For those who prefer the C++ numeric limits interface that would be:

#include <limits>

char buffer[4 + std::numeric_limits<double>::digits10 + -std::numeric_limits<double>::min_exponent10];
John Cummings
  • 1,949
  • 3
  • 22
  • 38
  • consider to use DBL_DECIMAL_DIG (instead of DBL_DIG) to get a string that you can convert back to the original double – MattTT Jul 11 '23 at 15:50
0

The maximum number of characters that will be required to print any decimal double value (i.e. in "%f" format) will be for the value of -DBL_MIN (i.e. -0x1p-1022, assuming binary64 IEEE 754 is your double). For that you'll need exactly 325 characters. That's: DBL_DIG + abs(DBL_MIN_10_EXP) + strlen("-0."). This is of course because log10(fabs(DBL_MIN)) is 308, which is also abs(DBL_MIN_10_EXP)+1 (the +1 is because of the leading digit to the left of the decimal place), and that's the number of leading zeros to the left of the significant digits.

int lz;                 /* aka abs(DBL_MIN_10_EXP)+1 */
int dplaces;
int sigdig;             /* aka DBL_DECIMAL_DIG - 1 */
double dbl = -DBL_MIN;

lz = abs((int) lrint(floor(log10(fabs(dbl)))));
sigdig = lrint(ceil(DBL_MANT_DIG * log10((double) FLT_RADIX)));
dplaces = sigdig + lz - 1;
printf("f = %.*f\n", dplaces, dbl);
Greg A. Woods
  • 2,663
  • 29
  • 26
  • My output is "f = -0.00000000(300 more zeros) 000000000000000" and `printf("lz = %d, sigdig = %d, dplaces = %d, dbl = %g\n", lz, sigdig, dplaces, dbl);` prints "lz = 308, sigdig = 16, dplaces = 323, dbl = -2.22507e-308". What output do you receive? – chux - Reinstate Monica Apr 27 '18 at 13:43
  • `lrint()` call provides little value versus just `(int)`. Removing it would add clarity. – chux - Reinstate Monica Apr 27 '18 at 13:46
  • The `sigdig` formula is off by 1 with `FLT_RADIX == 2`. See definition of `DBL_DECIMAL_DIG` which is effectively what `sigdig` should be to determine _significant digits_ for all `double`. Suggest `sigdig = DBL_DECIMAL_DIG;` (e.g. `17`) – chux - Reinstate Monica Apr 27 '18 at 13:49
  • Note, `DBL_MIN` is the smallest normal. `DBL_TRUE_MIN` is the smallest non-zero double. Of course with printing _significant digits_, using `DBL_MIN` will suffice - as this answer does. – chux - Reinstate Monica Apr 27 '18 at 13:56
  • As I said in the comment `sigdig` *is* `DBL_DECIMAL_DIG - 1` – Greg A. Woods Apr 28 '18 at 21:42
  • @GregA.Woods: But in your answer you write `DBL_DIG + abs(DBL_MIN_10_EXP) + strlen("-0.")` instead of `DBL_DECIMAL_DIG + abs(DBL_MIN_10_EXP) + strlen("-0.")` – MattTT Jul 11 '23 at 15:57
  • I agree that's a little confusing @MattTT, but it all depends on what the purpose is. The OP said "maximum number of chars required to represent", which could be interpreted to mean _"survive binary->decimal->binary round-trip conversion"_, in which case `DBL_DECIMAL_DIG` is correct, but in the text I said `DBL_DIG`, which could mean _"survive decimal->binary->decimal round-trip conversion"_. – Greg A. Woods Jul 13 '23 at 21:46
  • `DBL_DIG` [**does** mean](https://en.cppreference.com/w/cpp/header/cfloat) *"survive decimal->binary->decimal round-trip conversion"*. And `DBL_DECIMAL_DIG` does mean *"survive binary->decimal->binary round-trip conversion"*. And since the OP wants to convert into (decimal) text, the latter variant is the needed one to provide non-ambiguous results. Furthermore it needs more chars than the first one and therefore fulfills the requirement of "maximum number of chars". – MattTT Jul 14 '23 at 06:51
  • Well, the OP only said he wanted to avoid a buffer overflow, and in that case it doesn't matter which precision he chooses, so long as he uses the same one to print as he uses to calculate the buffer size. If he wants an exact sized buffer every time then the answer gets _far_ more complicated anyway. – Greg A. Woods Jul 14 '23 at 22:12
0

"For that you'll need exactly 325 characters"

Apparently (and this is a very common case) You don't understand the how the conversion between different numeric bases works.

No matter how accurate the definition of the DBL_MIN is, it is limited by hardware accuracy, which is usually up to 80 bits or 18 decimal digits (x86 and similar architectures)

For that reason, specialized arbitrary-precision-arithmetic libraries has been invented, like f.e. gmp or mpfr.

Zoe
  • 27,060
  • 21
  • 118
  • 148
0

You should represent floating point numbers as the shortest of either a decimal or in e notation, with 17 decimal digits of precision (with printf("%.17g\n", -1.2345);, this is also how Python does it), then the answer is 24 characters (+1 for the trailing null):

-2.2250738585072014e-308

If you're printing the value as a decimal with 17 digits of precision then the answer is 343 characters (+1 for null):

-0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000049406564584124654

If you're printing the exact value as a decimal (using this C code for example), then the answer is 1077 characters (+1 for null):

-0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004940656458412465441765687928682213723650598026143247644255856825006755072702087518652998363616359923797965646954457177309266567103559397963987747960107818781263007131903114045278458171678489821036887186360569987307230500063874091535649843873124733972731696151400317153853980741262385655911710266585566867681870395603106249319452715914924553293054565444011274801297099995419319894090804165633245247571478690147267801593552386115501348035264934720193790268107107491703332226844753335720832431936092382893458368060106011506169809753078342277318329247904982524730776375927247874656084778203734469699533647017972677717585125660551199131504891101451037862738167250955837389733598993664809941164205702637090279242767544565229087538682506419718265533447265625
Boris Verkhovskiy
  • 14,854
  • 11
  • 100
  • 103