I stumbled upon a very strange bug in PHP; this statement sends it into an infinite loop:

<?php $d = 2.2250738585072011e-308; ?>

(The same thing happens if you write the number without scientific notation — 324 decimal places.)

I hit this bug in the two places I tested for it: on Windows (PHP 5.3.1 under XAMPP 1.7.3), and on Linux (PHP Version 5.3.2-1ubuntu4.5) — both on an Intel Core Duo processor. I’ve written a bug report.

What’s Going On?

As a string, 2.2250738585072011e-308 causes no problems; it’s when it’s treated as a numeric value that the bug hits.

This works:

<?php $d = '2.2250738585072011e-308'; echo $d; ?>

but this doesn’t:

<?php $d = '2.2250738585072011e-308'; echo $d + 0; ?>

I don’t have a debug build of PHP available, but I did poke around in the source code (version 5.2.8). I found zend_strtod.c, which appears to be based on a very old version of David Gay’s strtod() function. I don’t know if zend_strtod() is even called in this case, but it was simple enough for me to isolate it and run it outside of PHP — under Visual C++ in fact. It did not hang on that input. (Update: zend_strtod() is called, and I can hang zend_strtod() in Visual C++ if I compile with all optimizations.)

What’s Special About 2.2250738585072011e-308?

2.2250738585072011e-308 represents the largest subnormal double-precision floating-point number; written as a hexadecimal floating-point constant, it’s 0x0.fffffffffffffp-1022. 2.2250738585072011e-308 is one of five 17-digit decimal values that convert (correctly) to 0x0.fffffffffffffp-1022:

2.2250738585072007e-308

2.2250738585072008e-308

2.2250738585072009e-308

2.2250738585072010e-308

2.2250738585072011e-308

Only 2.2250738585072011e-308 causes the problem. It happens to be the largest of the five decimal values, so I guess that matters somehow.

If you have any thoughts on what the bug is, please let me (or PHP) know.

Is This Serious?

I’ve gotten no response to my bug report yet, but I can’t help but wonder: is this serious? Could you bring down a Web server by entering this constant in a PHP-based form?

Update

Yes, it’s serious.

1/4/2011: There is a lot of discussion about this, in addition to the comments below; for example, see

And as pointed out in the comments, equivalent forms of the number also cause the problem:

Variations on the placement of the decimal point: 0.22250738585072011e-307, 22.250738585072011e-309, 22250738585072011e-324, etc.

Leading zeros: e.g., 0 2.2250738585072011e-308.

2.2250738585072011e-308. Trailing zeros: e.g., 2.2250738585072011 0 e-308.

e-308. Leading zeros in the exponent: e.g., 2.2250738585072011e- 0 308.

308. (Combinations of the above)

You can also alter the full 324 decimal place version of the number with leading and trailing zeros.

1/22/2011: I thought of another set of numbers that cause the same problem — numbers of 18 digits or more but with the same 17 digit prefix. For example:

2.22507385850720111e-308

2.225073858507201123e-308

2.22507385850720113099e-308

2.225073858507201100001e-308

2.2250738585072011123456789012345678901234567890123456789e-308

etc.

In decimal these numbers are distinct, but they all map to the same floating-point number.

It seems the only ones that cause a problem though are those whose 18th digit is 0, 1, 2, or 3 (2.22507385850720114e-308 doesn’t cause the problem, for example).

Update: The Bug Has Been Fixed

The bug fix was released on 1/6/2011; this is from the PHP news release:

This release resolves a critical issue … where conversions from string to double might cause the PHP interpreter to hang on systems using x87 FPU registers.

The problem is known to only affect x86 32-bit PHP processes, regardless of whether the system hosting PHP is 32-bit or 64-bit.

Update: An Analysis of the Bug and Its Fix

I’ve found the specific cause of the bug, and why the fix fixes it; please see my article “Why “Volatile” Fixes the 2.2250738585072011e-308 Bug.”

Update: The Real Problem Is the Old Copy of dtoa.c

This bug could have been avoided altogether if zend_strtod() was kept in sync with David Gay’s strtod(); see my article “A Better Fix for the PHP 2.2250738585072011e-308 Bug.”