问题
<?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?
回答1:
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 float1026.0
, then casts (superfluously) to float.
Edit: additionally, see:
- Force PHP integer overflow
- http://php.net/manual/en/language.types.integer.php
来源:https://stackoverflow.com/questions/31771152/why-two-float-variables-with-php-int-max-values-are-same-unless-one-of-them-is-a