Interestingly, programming languages vary in how much precision they allow in printed floating-point fractions. You would think they’d all be the same, allowing you to print as many decimal places as you ask for. After all, a floating-point fraction is a dyadic fraction; it has as many decimal places as it has bits in its fractional representation.

Consider the dyadic fraction 5,404,319,552,844,595/253. Its decimal expansion is 0.59999999999999997779553950749686919152736663818359375, and its binary expansion is 0.10011001100110011001100110011001100110011001100110011. Both are 53 digits long. The ideal programming language lets you print all 53 decimal places, because all are meaningful. Unfortunately, many languages won’t let you do that; they typically cap the number of decimal places at between 15 and 17, which for our example might be 0.59999999999999998.

I can guess why this is so. We view printed values as decimal numbers, not binary approximations. 0.59999999999999997779553950749686919152736663818359375 looks like 0.6, and in fact is what’s stored in a double-precision floating-point variable that is assigned the value 0.6 (it’s the closest double-precision approximation of 0.6). If you print this variable, you can live with it being 0.59, or a finite version thereof. But all those digits after the last 9 look like garbage, and in fact they are with respect to 0.6.

Now change your perspective. Assume you care about binary approximations. You might want to see which dyadic fraction a decimal fraction rounds to. You might want to print a variable that has been assigned a dyadic fraction explicitly. You might want to know the value of a small negative power of two. In these cases, you’ll want to see the entire decimal expansion, “garbage” and all.

Let’s see how good various programming languages are at giving you this precision.

Summary of Results

I tested the printing precision of eight different languages, some on both Windows and Linux, for a total of twelve environments. I determined the maximum precision each language allows when printing a double-precision floating-point value (AKA double). I picked two cases for each, the smallest and largest (positive) dyadic fractions that could be printed in their entirety.

The smallest fraction is a negative power of two. The smallest negative power of two that fits in a double is 2-1074. In decimal, it is 323 0s followed by 751 other digits. In binary, it is 1073 0s followed by a 1 (of course all those digits don’t fit in a double — they are implied by the exponent).

The largest fraction is of the form (2n – 1)/2n, or 1 – 2-n. For a double, the largest value possible is 1 – 2-53. In decimal, it is 15 9s followed by 38 other digits. In binary, it is 53 1s.

Here’s a summary of what I found (striked out text reflects updated versions — see below for details):

Longest Dyadic Fractions Printed in Full Precision Language OS Smallest Fraction Largest Fraction GCC (C) Linux 2-1074 1 – 2-53 Visual C++ Windows 2-24 1 – 2-17 Java Windows 2-24 1 – 2-16 Visual Basic Windows 2-21 1 – 2-15 PHP Windows 2-40 2-53 1 – 2-40 1 – 2-53 JavaScript (Firefox) Windows 2-100 1 – 2-53 JavaScript ( IE ) Windows 2-20 1 – 2-16 VBScript ( IE ) Windows 2-21 1 – 2-15 ActivePerl Windows 2-24 1 – 2-17 Python Windows 2-24 2-1074 1 – 2-17 1 – 2-53 Perl Linux 2-1074 1 – 2-53 Python Linux 2-117 1 – 2-53

The GNU Compiler Collection (GCC), and specifically its C compiler, is what I’ll call the gold standard. It prints all dyadic fractions to full precision. Perl on Linux, which presumably is built using GCC and glibc, does the same.

JavaScript in Firefox is curious. It prints the largest double fraction, 1 – 2-53, but only prints powers of two up to 2-100. It seems like an arbitrary maximum, perhaps due to a buffer size limit. PHP is similar. It picks an arbitrary maximum of 40 53 decimal places, regardless of the fraction. Python on Linux also has an interesting limit — it prints powers of two up to 2-117.

What follows are the details of how I determined the values in the table. For each value, I show two pieces of code: one that prints the longest fraction, and one that fails to print the next longer fraction. In other words, I determine the maximum by going beyond it.

1. GCC C (Linux)

Smallest Fraction

This code prints all 1,074 decimal places of 2-1074:

#include "stdio.h" int main() { /* Print 2^-1074 */ double dyadic = /* Compute as (2^-64)^16 * 2^-50 to break up */ 0.0000000000000000000542101086242752217003726400434970855712890625* 0.0000000000000000000542101086242752217003726400434970855712890625* 0.0000000000000000000542101086242752217003726400434970855712890625* 0.0000000000000000000542101086242752217003726400434970855712890625* 0.0000000000000000000542101086242752217003726400434970855712890625* 0.0000000000000000000542101086242752217003726400434970855712890625* 0.0000000000000000000542101086242752217003726400434970855712890625* 0.0000000000000000000542101086242752217003726400434970855712890625* 0.0000000000000000000542101086242752217003726400434970855712890625* 0.0000000000000000000542101086242752217003726400434970855712890625* 0.0000000000000000000542101086242752217003726400434970855712890625* 0.0000000000000000000542101086242752217003726400434970855712890625* 0.0000000000000000000542101086242752217003726400434970855712890625* 0.0000000000000000000542101086242752217003726400434970855712890625* 0.0000000000000000000542101086242752217003726400434970855712890625* 0.0000000000000000000542101086242752217003726400434970855712890625* 0.00000000000000088817841970012523233890533447265625; printf ("%1.1074f",dyadic); }

This code does not print 2-1075. This is expected. 2-1075 underflows to 0 in a double, so 1075 decimal places of 0 are printed instead:

#include "stdio.h" int main() { /* Print 2^-1075 */ double dyadic = /* Compute as (2^-64)^16 * 2^-51 to break up */ 0.0000000000000000000542101086242752217003726400434970855712890625* 0.0000000000000000000542101086242752217003726400434970855712890625* 0.0000000000000000000542101086242752217003726400434970855712890625* 0.0000000000000000000542101086242752217003726400434970855712890625* 0.0000000000000000000542101086242752217003726400434970855712890625* 0.0000000000000000000542101086242752217003726400434970855712890625* 0.0000000000000000000542101086242752217003726400434970855712890625* 0.0000000000000000000542101086242752217003726400434970855712890625* 0.0000000000000000000542101086242752217003726400434970855712890625* 0.0000000000000000000542101086242752217003726400434970855712890625* 0.0000000000000000000542101086242752217003726400434970855712890625* 0.0000000000000000000542101086242752217003726400434970855712890625* 0.0000000000000000000542101086242752217003726400434970855712890625* 0.0000000000000000000542101086242752217003726400434970855712890625* 0.0000000000000000000542101086242752217003726400434970855712890625* 0.0000000000000000000542101086242752217003726400434970855712890625* 0.000000000000000444089209850062616169452667236328125; printf ("%1.1075f",dyadic); }

Largest Fraction

This code prints all 53 decimal places of 1 – 2-53:

#include "stdio.h" int main() { /* Print 1 - 2^-53 */ double dyadic = 0.99999999999999988897769753748434595763683319091796875; printf ("%1.53f",dyadic); }

This code does not print 1 – 2-54; it prints 1 followed by 54 decimal places of 0s (the compiler, during decimal to binary conversion, rounds this “halfway” case up to 1.0):

#include "stdio.h" int main() { /* Print 1 - 2^-54 */ double dyadic = 0.999999999999999944488848768742172978818416595458984375; printf ("%1.54f",dyadic); }

2. Visual C++ (Windows)

Smallest Fraction

This code prints all 24 decimal places of 2-24:

#include "stdio.h" int main() { /* Print 2^-24 */ double dyadic = 0.000000059604644775390625; printf ("%1.24f",dyadic); }

This code does not print 2-25, but instead 0.0000000298023223876953130:

#include "stdio.h" int main() { /* Print 2^-25 */ double dyadic = 0.0000000298023223876953125; printf ("%1.25f",dyadic); }

Largest Fraction

This code prints all 17 decimal places of 1 – 2-17:

#include "stdio.h" int main() { /* Print 1 - 2^-17 */ double dyadic = 0.99999237060546875; printf ("%1.17f",dyadic); }

This code does not print 1 – 2-18, but instead 0.999996185302734380:

#include "stdio.h" int main() { /* Print 1 - 2^-18 */ double dyadic = 0.999996185302734375; printf ("%1.18f",dyadic); }

3. Java (Windows)

Smallest Fraction

This code prints all 24 decimal places of 2-24:

class precision { /* Print 2^-24 */ public static void main(String[] args) { double dyadic = 0.000000059604644775390625; System.out.printf("%1.24f

",dyadic); } }

This code does not print 2-25, but instead 0.0000000298023223876953120:

class precision { /* Print 2^-25 */ public static void main(String[] args) { double dyadic = 0.0000000298023223876953125; System.out.printf("%1.25f

",dyadic); } }

Largest Fraction

This code prints all 16 decimal places of 1 – 2-16:

class precision { /* Print 1 - 2^-16 */ public static void main(String[] args) { double dyadic = 0.9999847412109375; System.out.printf("%1.16f

",dyadic); } }

This code does not print 1 – 2-17, but instead 0.99999237060546880:

class precision { /* Print 1 - 2^-17 */ public static void main(String[] args) { double dyadic = 0.99999237060546875; System.out.printf("%1.17f

",dyadic); } }

4. Visual Basic (Windows)

Smallest Fraction

This code prints all 21 decimal places of 2-21:

Module Precision 'Print 2^-21 Sub Main() Dim dyadic As Double dyadic = 0.000000476837158203125 Console.WriteLine("{0:F21}", dyadic) End Sub End Module

This code does not print 2-22, but instead 0.0000002384185791015630:

Module Precision 'Print 2^-22 Sub Main() Dim dyadic As Double dyadic = 0.0000002384185791015625 Console.WriteLine("{0:F22}", dyadic) End Sub End Module

Largest Fraction

This code prints all 15 decimal places of 1 – 2-15:

Module Precision 'Print 1 - 2^-15 Sub Main() Dim dyadic As Double dyadic = 0.999969482421875 Console.WriteLine("{0:F15}", dyadic) End Sub End Module

This code does not print 1 – 2-16, but instead 0.9999847412109380:

Module Precision 'Print 1 - 2^-16 Sub Main() Dim dyadic As Double dyadic = 0.9999847412109375 Console.WriteLine("{0:F16}", dyadic) End Sub End Module

5. PHP (Windows)

(Updated to reflect the fix for bug 47168, as tested in PHP 5.3.5. Curiously, the arbitrary limit still exists, albeit raised from 40 to 53 decimal places. I wrote a new bug to address this, but it doesn’t look like they’ll be fixing that one.)

Smallest Fraction



This code prints all 40 decimal places of 2-40:

<?php /* Print 2^-40 */ $dyadic = 0.0000000000009094947017729282379150390625; printf ("%1.40f",$dyadic); ?>

This code does not print 2-41; 0.0000000000004547473508864641189575195312 is printed instead:

<?php /* Print 2^-41 */ $dyadic = 0.00000000000045474735088646411895751953125; printf ("%1.41f",$dyadic); ?>

This code prints all 53 decimal places of 2-53:

<?php /* Print 2^-53 */ $dyadic = 0.00000000000000011102230246251565404236316680908203125; printf ("%1.53f",$dyadic); ?>

This code does not print 2-54; instead, it prints

0.00000000000000005551115123125782702118158340454101562.

<?php /* Print 2^-54 */ $dyadic = 0.000000000000000055511151231257827021181583404541015625; printf ("%1.54f",$dyadic); ?>

Largest Fraction



This code prints all 40 decimal places of 1 – 2-40:

<?php /* Print 1 - 2^-40 */ $dyadic = 0.9999999999990905052982270717620849609375; printf ("%1.40f",$dyadic); ?>

This code does not print 1 – 2-41; instead, it prints 0.9999999999995452526491135358810424804688:

<?php /* Print 1 - 2^-41 */ $dyadic = 0.99999999999954525264911353588104248046875; printf ("%1.41f",$dyadic); ?>

This code prints all 53 decimal places of 1 – 2-53:

<?php /* Print 1 - 2^-53 */ $dyadic = 0.99999999999999988897769753748434595763683319091796875; printf ("%1.53f",$dyadic); ?>

This code does not print 1 – 2-54; it prints 1 followed by 53 decimal places of 0s:

<?php /* Print 1 - 2^-54 */ $dyadic = 0.999999999999999944488848768742172978818416595458984375; printf ("%1.54f",$dyadic); ?>

6. JavaScript in Firefox (Windows)

Smallest Fraction

This code prints all 100 decimal places of 2-100:

<script type="text/javascript"> /* Print 2^-100 */ var dyadic = /* Compute as 2^-50 * 2^-50 to break into 2 lines */ 0.00000000000000088817841970012523233890533447265625 * 0.00000000000000088817841970012523233890533447265625; document.write (dyadic.toFixed(100)); </script>

This code does not print 2-101. Instead, it prints nothing. The Firefox error console gives the message “precision 101 out of range,” complaining about toFixed(101):

<script type="text/javascript"> /* Print 2^-101 */ var dyadic = /* Compute as 2^-50 * 2^-51 to break into 2 lines */ 0.00000000000000088817841970012523233890533447265625 * 0.000000000000000444089209850062616169452667236328125; document.write (dyadic.toFixed(101)); </script>

This code is the same as above but with “toFixed(100).” It therefore can’t print 2-101. Instead it rounds the last two digits, ’25’, to ‘3’ (I didn’t want to paste a 100 digit decimal here):

<script type="text/javascript"> /* Print 2^-101 */ var dyadic = /* Compute as 2^-50 * 2^-51 to break into 2 lines */ 0.00000000000000088817841970012523233890533447265625 * 0.000000000000000444089209850062616169452667236328125; document.write (dyadic.toFixed(100)); </script>

Largest Fraction

This code prints all 53 decimal places of 1 – 2-53:

<script type="text/javascript"> /* Print 1 - 2^-53 */ var dyadic = 0.99999999999999988897769753748434595763683319091796875; document.write (dyadic.toFixed(53)); </script>

This code does not print 1 – 2-54, but instead 1 followed by 54 decimal places of 0s:

<script type="text/javascript"> /* Print 1 - 2^-54 */ var dyadic = 0.999999999999999944488848768742172978818416595458984375; document.write (dyadic.toFixed(54)); </script>

7. JavaScript in Internet Explorer (Windows)

Smallest Fraction

This code prints all 20 decimal places of 2-20:

<script type="text/javascript"> /* Print 2^-20*/ var dyadic = 0.00000095367431640625; document.write (dyadic.toFixed(20)); </script>

This code does not print 2-21. Instead, it prints nothing. The Internet Explorer error console gives the message “Error: The number of fractional digits is out of range,” complaining about toFixed(21):

<script type="text/javascript"> /* Print 2^-21*/ var dyadic = 0.000000476837158203125; document.write (dyadic.toFixed(21)); </script>

This code is the same as above but with “toFixed(20).” It therefore can’t print 2-21. Instead it prints 0.00000047683715820313:

<script type="text/javascript"> /* Print 2^-21*/ var dyadic = 0.000000476837158203125; document.write (dyadic.toFixed(20)); </script>

Largest Fraction

This code prints all 16 decimal places of 1 – 2-16:

<script type="text/javascript"> /* Print 1 - 2^-16*/ var dyadic = 0.9999847412109375; document.write (dyadic.toFixed(16)); </script>

This code does not print 1 – 2-17, but instead 0.99999237060546870:

<script type="text/javascript"> /* Print 1 - 2^-17 */ var dyadic = 0.99999237060546875; document.write (dyadic.toFixed(17)); </script>

8. VBScript in Internet Explorer (Windows)

Smallest Fraction

This code prints all 21 decimal places of 2-21:

<script type="text/vbscript"> 'Print 2^-21 dyadic = 0.000000476837158203125 document.write(FormatNumber(dyadic,21)) </script>

This code does not print 2-22, but instead 0.0000002384185791015630:

<script type="text/vbscript"> 'Print 2^-22 dyadic = 0.0000002384185791015625 document.write(FormatNumber(dyadic,22)) </script>

Largest Fraction

This code prints all 15 decimal places of 1 – 2-15:

<script type="text/vbscript"> 'Print 1 - 2^-15 dyadic = 0.999969482421875 document.write(FormatNumber(dyadic,15)) </script>

This code does not print 1 – 2-16, but instead 0.9999847412109380:

<script type="text/vbscript"> 'Print 1 - 2^-16 dyadic = 0.9999847412109375 document.write(FormatNumber(dyadic,16)) </script>

9. ActivePerl (Windows)

Smallest Fraction

This code prints all 24 decimal places of 2-24:

# Print 2^-24 $dyadic = 0.000000059604644775390625; printf "%1.24f",$dyadic;

This code does not print 2-25, but instead 0.0000000298023223876953130:

# Print 2^-25 $dyadic = 0.0000000298023223876953125; printf "%1.25f",$dyadic;

Largest Fraction

This code prints all 17 decimal places of 1 – 2-17:

# Print 1 - 2^-17 $dyadic = 0.99999237060546875; printf "%1.17f",$dyadic;

This code does not print 1 – 2-18, but instead 0.999996185302734380:

# Print 1 - 2^-18 $dyadic = 0.999996185302734375; printf "%1.18f",$dyadic;

10. Python (Windows)

(Updated to reflect Python 3.1.)

Smallest Fraction



This code prints all 24 decimal places of 2-24:

# Print 2^-24 dyadic = 0.000000059604644775390625 print(format(dyadic,"1.24f"))

This code does not print 2-25, but instead 0.0000000298023223876953130:

# Print 2^-25 dyadic = 0.0000000298023223876953125 print(format(dyadic,"1.25f"))

This code prints all 1,074 decimal places of 2-1074:

#Print 2^-1074 (Compute as (2^-50)^21 * 2^-24 to break up) dyadic = 0.00000000000000088817841970012523233890533447265625 dyadic *= 0.00000000000000088817841970012523233890533447265625 dyadic *= 0.00000000000000088817841970012523233890533447265625 dyadic *= 0.00000000000000088817841970012523233890533447265625 dyadic *= 0.00000000000000088817841970012523233890533447265625 dyadic *= 0.00000000000000088817841970012523233890533447265625 dyadic *= 0.00000000000000088817841970012523233890533447265625 dyadic *= 0.00000000000000088817841970012523233890533447265625 dyadic *= 0.00000000000000088817841970012523233890533447265625 dyadic *= 0.00000000000000088817841970012523233890533447265625 dyadic *= 0.00000000000000088817841970012523233890533447265625 dyadic *= 0.00000000000000088817841970012523233890533447265625 dyadic *= 0.00000000000000088817841970012523233890533447265625 dyadic *= 0.00000000000000088817841970012523233890533447265625 dyadic *= 0.00000000000000088817841970012523233890533447265625 dyadic *= 0.00000000000000088817841970012523233890533447265625 dyadic *= 0.00000000000000088817841970012523233890533447265625 dyadic *= 0.00000000000000088817841970012523233890533447265625 dyadic *= 0.00000000000000088817841970012523233890533447265625 dyadic *= 0.00000000000000088817841970012523233890533447265625 dyadic *= 0.00000000000000088817841970012523233890533447265625 dyadic *= 0.000000059604644775390625 print(format(dyadic,"1.1074f"))

Largest Fraction



This code prints all 17 decimal places of 1 – 2-17:

# Print 1 - 2^-17 dyadic = 0.99999237060546875 print(format(dyadic,"1.17f"))

This code does not print 1 – 2-18, but instead 0.999996185302734380:

# Print 1 - 2^-18 dyadic = 0.999996185302734375 print(format(dyadic,"1.18f"))

This code prints all 53 decimal places of 1 – 2-53:

# Print 1 - 2^-53 dyadic = 0.99999999999999988897769753748434595763683319091796875 print(format(dyadic,"1.53f"))

11. Perl (Linux)

Smallest Fraction

This code prints all 1,074 decimal places of 2-1074:

# Print 2^-1074 $dyadic = #Compute as (2^-64)^16 * 2^-50 to break up 0.0000000000000000000542101086242752217003726400434970855712890625* 0.0000000000000000000542101086242752217003726400434970855712890625* 0.0000000000000000000542101086242752217003726400434970855712890625* 0.0000000000000000000542101086242752217003726400434970855712890625* 0.0000000000000000000542101086242752217003726400434970855712890625* 0.0000000000000000000542101086242752217003726400434970855712890625* 0.0000000000000000000542101086242752217003726400434970855712890625* 0.0000000000000000000542101086242752217003726400434970855712890625* 0.0000000000000000000542101086242752217003726400434970855712890625* 0.0000000000000000000542101086242752217003726400434970855712890625* 0.0000000000000000000542101086242752217003726400434970855712890625* 0.0000000000000000000542101086242752217003726400434970855712890625* 0.0000000000000000000542101086242752217003726400434970855712890625* 0.0000000000000000000542101086242752217003726400434970855712890625* 0.0000000000000000000542101086242752217003726400434970855712890625* 0.0000000000000000000542101086242752217003726400434970855712890625* 0.00000000000000088817841970012523233890533447265625; printf "%1.1074f",$dyadic;

This code does not print 2-1075. This is expected. 2-1075 underflows to 0 in a double, so 1075 decimal places of 0 are printed instead:

# Print 2^-1075 $dyadic = #Compute as (2^-64)^16 * 2^-51 to break up 0.0000000000000000000542101086242752217003726400434970855712890625* 0.0000000000000000000542101086242752217003726400434970855712890625* 0.0000000000000000000542101086242752217003726400434970855712890625* 0.0000000000000000000542101086242752217003726400434970855712890625* 0.0000000000000000000542101086242752217003726400434970855712890625* 0.0000000000000000000542101086242752217003726400434970855712890625* 0.0000000000000000000542101086242752217003726400434970855712890625* 0.0000000000000000000542101086242752217003726400434970855712890625* 0.0000000000000000000542101086242752217003726400434970855712890625* 0.0000000000000000000542101086242752217003726400434970855712890625* 0.0000000000000000000542101086242752217003726400434970855712890625* 0.0000000000000000000542101086242752217003726400434970855712890625* 0.0000000000000000000542101086242752217003726400434970855712890625* 0.0000000000000000000542101086242752217003726400434970855712890625* 0.0000000000000000000542101086242752217003726400434970855712890625* 0.0000000000000000000542101086242752217003726400434970855712890625* 0.000000000000000444089209850062616169452667236328125; printf "%1.1075f",$dyadic;

Largest Fraction

This code prints all 53 decimal places of 1 – 2-53:

# Print 1 - 2^-53 $dyadic = 0.99999999999999988897769753748434595763683319091796875; printf "%1.53f",$dyadic;

This code does not print 1 – 2-54, but instead 1 followed by 54 decimal places of 0s:

# Print 1 - 2^-54 $dyadic = 0.999999999999999944488848768742172978818416595458984375; printf "%1.54f",$dyadic;

12. Python (Linux)

Smallest Fraction

This code prints all 117 digits of 2-117:

# Print 2^-117 # Compute as (2^-32)^3 * 2^-21 to break up dyadic = 0.00000000023283064365386962890625 dyadic *= 0.00000000023283064365386962890625 dyadic *= 0.00000000023283064365386962890625 dyadic *= 0.000000476837158203125 print(format(dyadic,"1.117f"))

This code does not print 2-118. Instead, it prints the first 117 digits of 2-118 (it cuts off the last digit):

# Print 2^-118 # Compute as (2^-32)^3 * 2^-22 to break up dyadic = 0.00000000023283064365386962890625 dyadic *= 0.00000000023283064365386962890625 dyadic *= 0.00000000023283064365386962890625 dyadic *= 0.0000002384185791015625 print(format(dyadic,"1.118f"))

Largest Fraction

This code prints all 53 decimal places of 1 – 2-53:

# Print 1 - 2^-53 dyadic = 0.99999999999999988897769753748434595763683319091796875 print(format(dyadic,"1.53f"))

This code prints 1 – 2-53 with a 0 appended (the interpreter, during decimal to binary conversion, appears to be rounding this “halfway” case down to 1 – 2-53):

# Print 1 - 2^-54 dyadic = 0.999999999999999944488848768742172978818416595458984375 print(format(dyadic,"1.54f"))

Should They All Be Like GCC?

Should all languages allow printing to maximum precision like GCC? I don’t see why not. Granted, there’s probably not much floating-point being done in scripting languages, but why not support it if it’s easy to implement? One unknown is compatibility. Are there programs that depend on the current imprecise output?

I tested the waters by submitting bug reports for Visual C++ and PHP. Microsoft took over ten months to consider my request but they acknowledged the problem and said they would change it in a future release (it seems to apply to all of Visual Studio by the way). On the other hand, PHP took only three hours to reject my request without a good explanation (Update: Rasmus reopened the bug.)

I don’t plan on submitting more bug reports at the moment, but if you decide to, let us know so we can track it.

Other Languages and Environments

There are lots of things I didn’t try, like Fortran, Java on Linux, JavaScript in Opera, Safari, and Chrome, etc. If you are so inclined, please experiment and let us know what you find.