5
<?php
$x=PHP_INT_MAX;
echo ((float)($x+1026)==(float)($x))?'EQUAL':'Not Equal';

I know floating point arithmetic is not exact and $x and $x+1 are so close together that they are rounded to the same floating point value and it shows the output as EQUAL if you use any number between 1 and 1025 but its only after you use value beyond 1025 it will start giving output as 'Not Equal'. I want to know why? What's the reason behind it? Why only after 1025?

Varun Krish
  • 529
  • 4
  • 14
  • 2
    You need to start looking at the [bit-level representation of floats](https://en.wikipedia.org/wiki/Floating_point) and this will also vary depending on whether you're running 32-bit or 64-bit PHP – Mark Baker Aug 02 '15 at 11:01
  • which PHP version do you have ? with my test I have "not equal" with "$x + 10" for exemple – mmm Aug 02 '15 at 11:06
  • 1
    Take a look at [this convertor](http://www.h-schmidt.net/FloatConverter/IEEE754.html), which allows you to see the float representation for different numbers – Mark Baker Aug 02 '15 at 11:06
  • @mmm: my PHP version is 5.5.24 its Mac OS X 64 bit, I don't think float response is php version dependent, its something else. – Varun Krish Aug 02 '15 at 11:15
  • @MarkBaker:Thanks for references, I will check it & get back to you. – Varun Krish Aug 02 '15 at 11:20
  • 1
    Just because you're on a 64-bit operating system, doesn't automatically mean that you're running 64-bit PHP. You can check using the [PHP_INT_SIZE or PHP_INT_MAX constants](http://php.net/manual/en/reserved.constants.php) - on 32-bit PHP, PHP_INT_MAX is 2147483647; on 64-bit, it's 9223372036854775807; while PHP_INT_SIZE is 4 on 32-bit PHP, and 8 on 64-bit PHP – Mark Baker Aug 02 '15 at 13:17
  • @MarkBaker:I have already checked it while working on it,my PHP_INT_MAX is 9223372036854775807 and PHP_INT_SIZE is 8 – Varun Krish Aug 02 '15 at 13:52

1 Answers1

4

With float, your assumption $x == $x + 1 is not necessarily true:

$x=2;
echo ((float)($x+1)==(float)($x))?'EQUAL':'Not Equal';

yields "Not Equal".

In the converter linked in the comments (http://www.h-schmidt.net/FloatConverter/IEEE754.html), you can reproduce this. decimal 2.0 yields 0x40000000, decimal 3.0 yields 0x40400000, so they're indeed different when it comes to IEEE754 float representation.

Whereas, e.g., decimal 0.1 cannot be represented as float: 0x3dcccccd, which is 0.10000000149011612.

What's decimal 9223372036854775807? That's 0x5f000000, which is 9.223372E18, which is 9223372180000000000.

What's decimal 9223372036854775808 (PHP_MAX_INT + 1)? That's 0x5f000000, too.

What's decimal 9223372036854776832 (PHP_MAX_INT + 1025)? That's 0x5f000000, too.

What's decimal 9223372036854776833 (PHP_MAX_INT + 1026)? That's 0x5f000000, too.

They're all the same.

Whereas, e.g.: decimal 9223373000000000000 (PHP_MAX_INT + 963145224193)? That's 0x5f000001, which is 9.223373E18, which is 9223373000000000000.

Now, why does:

((float)($x+1025)==(float)($x+1026))?'EQUAL':'Not Equal';

yield "Not Equal"?

You're adding an integer to PHP_MAX_INT.

$x=PHP_INT_MAX;
$y=PHP_INT_MAX-1;
$z=PHP_INT_MAX+1;
var_dump($x);
var_dump($y);
var_dump($z);

yields:

int(9223372036854775807)
int(9223372036854775806)
float(9.2233720368548E+18)

PHP implicitly converts integers too large to float. And that's where you're basically lost in PHP internals (at least in my opinion), because from here, you'll never know what will happen (without knowing PHP internals, feel free to correct me, though).

Notice this:

$x=PHP_INT_MAX;
$a=(float)($x+1025.0); // 1025 float
$b=(float)($x+1026.0); // 1026 float
$c=(float)($x+1025); // 1025 int
$d=(float)($x+1026); // 1026 int
var_dump($x);
var_dump($a);
var_dump($b);
var_dump($c);
var_dump($d);
var_dump($a==$b);
var_dump($a===$b);
var_dump($c==$d);
var_dump($c===$d);

yields:

int(9223372036854775807)
float(9.2233720368548E+18)
float(9.2233720368548E+18)
float(9.2233720368548E+18)
float(9.2233720368548E+18)
bool(true)
bool(true)
bool(false)
bool(false)

If you add an integer ($x+1026) to PHP_MAX_INT, it is converted to float, and when you add a float ($x+1026.0), it is float, too, of course. But, obviously, they're not the same internally, see the comparisons above.

Bottom line:

  • Don't compare floats for equality
  • Be careful about your casts; (float)($x+1026) is an integer addition, and afterwards casted to float, whereas (float)($x+1026.0) converts $x to float, then adds the float 1026.0, then casts (superfluously) to float.

Edit: additionally, see:

Community
  • 1
  • 1
stef77
  • 1,000
  • 5
  • 19