views:

123

answers:

5
$onethird = 1.0/3;
$fivethirds = 1.0/3+1.0/3+1.0/3+1.0/3+1.0/3;
$half = 1.0/2;
$threehalf = 1.0/2+1.0/2+1.0/2;
var_dump($onethird + $fivethirds == $half + $threehalf);

which outputs false,but as we all know:5/3+1/3=2=3/2+1/2

How to fix this problem?

+3  A: 

The problem comes from the small errors introduced by Floating Point arithmetic. This is not specific to PHP.

You can fix this by introducing a small "tolerance" factor, i.e. by checking that the first value in the comparaision is >= the second value minus the tolerance and <= the second value plus the tolerance.

mjv
+5  A: 

This is one of the problems with the IEEE 754 representation of floating point numbers; the representations are not accurate enough to represent all rational numbers.

The way to do it is to compare the difference against a very small number for closeness, rather than equality:

abs(($onethird + $fivethirds) - ($half + $threehalf)) < 1e-8
Ignacio Vazquez-Abrams
This is the nature of floating-point numbers in general, so what does it have to do with the IEEE 754 standard?
Ignas R
PHP is written in C and therefore gets its FP support from that, and most C runtimes use IEEE 754.Also, IEEE 854 uses a variable radix and so can support more accuracy for some numbers at the cost of some processing speed when calculating with them.
Ignacio Vazquez-Abrams
It's the nature of floating point numbers in general. The value of epsilon (here 0.000000001) depends on the number of bits representing the float.
slebetman
+2  A: 

It depends of how precise your comparison should be
In this case, you should use bccomp() instead of "=="
see http://php.net/manual/en/language.types.float.php

alemjerus
+1  A: 
var_dump(abs($onethird + $fivethirds - $half + $threehalf) < 0.00001);

see: http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm also: http://docs.sun.com/source/806-3568/ncg_goldberg.html

This is true in all programming languages.

I have not yet come accross any language where this is done automatically when programmers want equality of floats. Someone should come up with a new operator, perhaps =~= for float equality which would automatically do the comparison of diff with epsilon:

if ($float1 =~= $float2) {...

It is annoying that every year, since I graduated in 2000, around this time of the year some newbie will ask this question on some newsgroup, forum or mailing list. Just last month I answered this on comp.lang.tcl. And it's not just newbies, two months ago I had to explain this to my coworker who has been developing software for over 5 years asking me why his Perl code is doesn't work.

slebetman
+1  A: 

How to fix this problem?

What actual Problem do you want to solve? Your code just shows that floating point numbers have limited accuracy - that's nothing new.

For most real-world applications, the input that isn't 100% accurate anyway, and the result just needs to be accurate to a couple of decimal places. Equality comparisons simply aren't something you need most of the time. If you do, you can fudge it by seeing whether the result is within some predefined small distance from a given number.

If you need decimal math with specific precision, use the BC Math functions - but realize that those are very slow if used for complex calculations.

Michael Borgwardt