0

Possible Duplicate:
compare floats in php

i have a condition:

if($x <= .3)
   echo 1;

it will echo 1 only if x is less than .3

if $x is EQUAL to .3, i do not get a 1.

i have tried wrapping the x in floatval(), but no luck

i tried to echo the $x and i get "0.3"

i have tried if ($x == .3) - nothing

if i have tried if (.3 == .3) which obviously works

any ideas? is this a PHP bug?

Community
  • 1
  • 1
dan
  • 97
  • 1
  • 1
  • 7

6 Answers6

4

It's all about binary representation of floating point numbers :

var_dump(sprintf("%.40f", 0.3));
// string(42) "0.2999999999999999888977697537484345957637"

Basically, 0.3 can't be represented exactly in base 2, so it gets truncated after a few digits. It's basically like 1/3 in base 10 : you can type 0.3, but 0.33 is more precise, so is 0.333, 0.3333, etc. You can't represent it exactly.

Vincent Savard
  • 34,979
  • 10
  • 68
  • 73
1

Floating point values are not exact. You can check to see if it's roughly <= 0.3 like this:

if ($x <= 0.3000001) {
    echo 'yay';
}
dkamins
  • 21,450
  • 7
  • 55
  • 59
  • wow - why is that like that? so 12.8-12.5 is really not .3 - even though it prints as .3? i have to check with the client, but hopefully he is only working in 2 digit accuracy – dan Nov 20 '10 at 01:34
  • This is a pretty competent explanation that builds on itself as it goes along: http://support.microsoft.com/kb/42980 – dkamins Nov 20 '10 at 01:53
  • BTW you don't really need to understand exactly why unless you're especially curious. The main thing to keep in mind is the general rule that 99% of the time, floating point numbers should not be compared directly with eqaulity operators. Integers can be of course (because they can be represented directly in binary). – dkamins Nov 20 '10 at 01:56
1

Here you go, big, red and fat: Floating Point Numbers:

It is typical that simple decimal fractions like 0.1 or 0.7 cannot be converted into their internal binary counterparts without a small loss of precision. This can lead to confusing results: for example, floor((0.1+0.7)*10) will usually return 7 instead of the expected 8, since the internal representation will be something like 7.9.

This is due to the fact that it is impossible to express some fractions in decimal notation with a finite number of digits. For instance, 1/3 in decimal form becomes 0.3.

So never trust floating number results to the last digit, and never compare floating point numbers for equality. If higher precision is necessary, the arbitrary precision math functions and gmp functions are available.

PS: There is even more fun:

INF == INF => false
INF < INF  => true
INF > INF  => true

So infinity is not infinity and infinity is smaller than infinity and greater than infinity at the same time. If you think about it, it does actually make some sense...

NikiC
  • 100,734
  • 37
  • 191
  • 225
0

hmmm, i have to assume that your var $x is really not equal to .3.

i just tested this:

<?
    $var = 0.3;

    if( $var <= .3 )
    {
        echo 'yay';
    }
    else
    {
        echo 'boo';
    }
?>

and it outputs yay

gthmb
  • 808
  • 5
  • 10
0

$x = .4;
if ($x <= .3) { echo 1; }
Works fine here...tried different values at $x
however... where are you getting $x ?? you might need to "round" the value before comparing.

Filipe YaBa Polido
  • 1,656
  • 1
  • 17
  • 39
0

Floating points are not 100% accurate. In short, the fractional component is generally stored by adding 1/(2^n) together. e.g., 1/2 + 1/4 is how 3/4 would be stored. So this isn't a bug, nor is it a specific PHP question.

However, this should always be true:

$x = 0.3;
if ($x == 0.3) echo "true";

because the same inaccuracy would be present in both.

But this is not guaranteed to be true:

$x = 0.1;
$y = 0.2;
if ($x + $y == 0.3) echo "true";

A simple way to work around this is to use a delta:

if (abs($a - $b) < $delta) echo "true"

where $delta is a very small number.

If you need accuracy, then check out something like the BCMath extension.

If this is for money, then it's usually easier to just do calculations in whole cents (integers), where $1.23 = 123.

Matthew
  • 47,584
  • 11
  • 86
  • 98