Take the 2-minute tour ×
$a = '35';
$b = '-34.99';
echo ($a + $b);

Results in 0.009999999999998

What is up with that? I wondered why my program kept reporting odd results.

Why doesn't PHP return the expected 0.01?

share|improve this question
I'd suggest reading about floating point numbers. Specifically the sections "Representable numbers, conversion and rounding" and "Accuracy Problems". The rest of the article is good if you want to understand how they work, but those two sections apply specifically to your question... –  ircmaxell Sep 16 '10 at 12:44
It's also worth noting that you are using strings instead of numbers (they get implicitly converted, but still). Do $a = 35; $b = -34.99 instead. –  NullUserException Sep 16 '10 at 13:41
add comment

8 Answers

up vote 76 down vote accepted

Because floating point arithmetic != real number arithmetic. An illustration of the difference due to imprecision is, for some floats a and b, (a+b)-b != a. This applies to any language using floats. See an example in Python.

Since floating point are binary numbers with finite precision, there's a finite amount of representable numbers, which leads accuracy problems and surprises like this. Here's another interesting read: What Every Computer Scientist Should Know About Floating-Point Arithmetic.

Back to your problem, basically there is no way to accurately represent 34.99 or 0.01 in binary (just like in decimal, 1/3 = 0.3333...), so approximations are used instead. To get around the problem, you can:

  1. Use round($result, 2) on the result to round it to 2 decimal places.

  2. Use integers. If that's currency, store them as 3500 and 3499. Then divide the result by 100.

It's a pity that PHP doesn't have a decimal datatype like other languages do.

share|improve this answer
i would add that 0.01 also can't be represented as is. this should be marked as correct, because it gives explanation and how to fix. but for increasing usefulness of it please explain a bit why fp != real, with all that binary stuff and precision lose –  Andrey Sep 16 '10 at 12:52
I would +1 again for that edit if I could. Hit the nail on the head... –  ircmaxell Sep 16 '10 at 13:03
@irc Thanks. I incorporated bits of your comment into the answer –  NullUserException Sep 16 '10 at 13:11
One pedantic note: There is a finite set of floats a and b where (a+b)-b == a. They simply need to have both a prime factor of 2, and be representable in the appropriate number of bits (about 7 decimal digits for single precision, 16 for double). So a = 0.5 and b = 0.25 works (and will always work for systems with 32 bit single precision floats). For floats that don't fit either or both of those preconditions, then (a+b)-b != a. But if both a and b fit those preconditions, then (a+b)-b == a should be true (but it's a finite set)... –  ircmaxell Sep 16 '10 at 13:17
@irc True; I used the wrong word there. –  NullUserException Sep 16 '10 at 13:19
show 3 more comments

Floating point numbers, like all numbers, must be stored in memory as a string of 0's and 1's. It's all bits to the computer. How floating point differs from integer is in how we interpret the 0's and 1's when we want to look at them.

One bit is the "sign" (0 = positive, 1 = negative), 8 bits are the exponent (ranging from -128 to +127), 23 bits are the number known as the "mantissa". So the binary representation of (S1)(P8)(M23) has the value (-1^S)M*2^P

The "mantissa" takes on a special form. In normal scientific notation we display the "one's place" along with the fraction. For instance:

4.39 x 10^3 = 439

In binary the "one's place" is a single bit. Since we ignore all the left-most 0's in scientific notation (we ignore any insignificant figures) the first bit is guaranteed to be a 1

1.101 x 2^4 = 1101 = 13

Since we are guaranteed that the first bit will be a 1, we remove this bit when storing the number to save space. So the above number is stored as just 101 (for the mantissa). The leading 1 is assumed

As an example, let's take the binary string


Breaking it into it's components:

Sign    Power           Mantissa
 0     00000100   10110000000000000000000
 +        +4             1.1011
 +        +4       1 + .5 + .125 + .0625
 +        +4             1.6875

Applying our simple formula:


In other words, 00000010011011000000000000000000 is 27 in floating point (according to IEEE-754 standards).

For many numbers there is no exact binary representation, however. Much like how 1/3 = 0.333.... repeating forever, 1/100 is 0.00000010100011110101110000..... with a repeating "10100011110101110000". A 32-bit computer can't store the entire number in floating point, however. So it makes its best guess.


Sign    Power           Mantissa
 +        -7     1.01000111101011100001010
 0    -00000111   01000111101011100001010
 0     11111001   01000111101011100001010

(note that negative 7 is produced using 2's complement)

It should be immediately clear that 01111100101000111101011100001010 looks nothing like 0.01

More importantly, however, this contains a truncated version of a repeating decimal. The original decimal contained a repeating "10100011110101110000". We've simplified this to 01000111101011100001010

Translating this floating point number back into decimal via our formula we get 0.0099999979 (note that this is for a 32-bit computer. A 64-bit computer would have much more accuracy)

When the Operating System sees a number like this, it has to determine if it should use the number as it sees it, or if it should round to a more meaningful number. Most computers will use the number as it's stored, since this is considered "safer". Some computers, however (Windows does this, I believe) will "round" to the nearest "simple" decimal number. In this case, 0.01 looks like a good candidate.

share|improve this answer
+1 thanks, now I know how floats are stored. PS: No, Windows doesn't. At least in PHP5.3.1/Win7 I did have floating point issues ;) –  NikiC Sep 29 '10 at 16:56
add comment

There's plenty of answers here about why floating point numbers work the way they do...

But there's little talk of arbitrary precision (Pickle mentioned it). If you want (or need) exact precision, the only way to do it (for rational numbers at least) is to use the BC Math extension (which is really just a BigNum, Arbitrary Precision implementation...

To add two numbers:

$number = '12345678901234.1234567890';
$number2 = '1';
echo bcadd($number, $number2);

will result in 12345678901235.1234567890...

This is called arbitrary precision math. Basically all numbers are strings which are parsed for every operation and operations are performed on a digit by digit basis (think long division, but done by the library). So that means it's quite slow (in comparison to regular math constructs). But it's very powerful. You can multiply, add, subtract, divide, find modulo and exponentiate any number that has an exact string representation.

So you can't do 1/3 with 100% accuracy, since it has a repeating decimal (and hence isn't rational).

But, if you want to know what 1500.0015 squared is:

Using 32 bit floats (double precision) gives the estimated result of:


But bcmath gives the exact answer of:


It all depends on the precision you need.

Also, something else to note here. PHP can only represent either 32 bit or 64 bit integers (depending on your install). So if an integer exceeds the size of the native int type (2.1 billion for 32bit, 9.2 x10^18, or 9.2 billion billion for signed ints), PHP will convert the int into a float. While that's not immediately a problem (Since all ints smaller than the precision of the system's float are by definition directly representable as floats), if you try multiplying two together, it'll lose significant precision.

For example, given $n = '40000000002':

As a number, $n will be float(40000000002), which is fine since it's exactly represented. But if we square it, we get: float(1.60000000016E+21)

As a string (using BC math), $n will be exactly '40000000002'. And if we square it, we get: string(22) "1600000000160000000004"...

So if you need the precision with large numbers, or rational decimal points, you might want to look into bcmath...

share|improve this answer
Nitpick: a number, such as 1/3, can have repeating decimal representation and still be rational. "Rational numbers" are all numbers that can be presented as a fraction of two numbers a and b where both a and b are integers. And 1/3 is indeed an example of such a number. –  Mikko Rantalainen Feb 16 '12 at 7:56
+1 i came here looking for a method of dividing one huge string by another and found bcmath in your answer. thanks! –  mulllhausen Oct 31 '13 at 4:19
add comment

Use PHP's round() function: http://php.net/manual/en/function.round.php

This answer solves problem, but not explains why. I thought that it is obvious [I am also programming in C++, so it IS obvious for me ;]], but if not, let's say that PHP has it's own calculating precision and in that particular situation it returned most complying information regarding that calculation.

share|improve this answer
-1 Because it is absolutely not an answer to the question. –  Dennis Haarbrink Sep 16 '10 at 12:46
@Dennis Haarbrink well, you downvoted this, someone downvoted my answer. well, so what is answer then? –  Andrey Sep 16 '10 at 12:49
@Andrey: Yeah, don't know why your answer got downvoted since it's pretty much the correct answer :) The best answer IMHO is by @ircmaxell in the comments on the OP. –  Dennis Haarbrink Sep 16 '10 at 12:53
That is not what the OP asked. PS: I didn't downvote you. –  NullUserException Sep 16 '10 at 12:56
@Tomasz Kowalczyk: Well, you have received 3 up and 2 down votes, that totals 26rep. I figure that should be enough for your answer :) –  Dennis Haarbrink Sep 16 '10 at 13:15
show 4 more comments

my php returns 0.01... alt text

maybe it have todo with php version, (i using 5.2)

share|improve this answer
are you working on a 64 bit machine with a 64 bit version of PHP? –  ircmaxell Sep 16 '10 at 12:47
I also get 0.01, Ubuntu, PHP 5.3.2-1ubuntu4.2. Is a 32bit install. –  Adirael Sep 16 '10 at 12:51
I get 0.0099....8 on RHEL 5.3.2 32 bit (Intel Xeon processor via VMWare hypervisor). But that's floating points for you... –  ircmaxell Sep 16 '10 at 12:54
well, i think this is at least comment. Writing "Works on my machine!" is useless. codinghorror.com/blog/2007/03/… –  Andrey Sep 16 '10 at 13:01
The reason is works on some machines and not others is because PHP doesn't directly partition memory. It calls C libraries which are built into the Operating System, which speaks to the ISA through an abstraction layer and translates into machine instructions. Assuming the Operating System follows IEEE-754 floating point standards, the processor you use will have no effect on the output, but how the Operating System's abstraction layer interprets the result and how the C libraries treat the result might. Windows will round any repeating binary to the nearest non-repeating decimal. –  stevendesu Sep 29 '10 at 2:38
add comment

Because 0.01 can't be represented exactly as sum of series of binary fractions. And that is how floats are stored in memory.

I guess it is not what you want to hear, but it is answer to question. For how to fix see other answers.

share|improve this answer
Sum of series of binary what-now? That's not how floats are stored. A float is essentially scientific notation in binary. One bit is the "sign" (0 = positive, 1 = negative), 8 bits are the exponent (ranging from -128 to +127), 23 bits are the number known as the "mantissa". So the binary representation of (S1)(P8)(M23) has the value (-1^S)M*2^P –  stevendesu Sep 29 '10 at 2:35
@steven_desu thank you for lesson. key part here is that mantissa is stored as binary fraction. it is answer to question "why" can't decimal fractions be stored precisely. –  Andrey Sep 29 '10 at 9:18
add comment

bcadd() might be useful here.


$a = '35';
$b = '-34.99';

echo $a + $b;
echo '<br />';
echo bcadd($a,$b,2);


(inefficient output for clarity)

First line gives me 0.009999999999998. Second gives me 0.01

share|improve this answer
add comment

wouldn't it be easier to use number_format(0.009999999999998, 2) or $res = $a+$b; -> number_format($res, 2);?

share|improve this answer
add comment

Your Answer


