0

I've got a few variables stored in an object that (for purposes of troubleshooting) I am extracting and turning into floats for comparison. For some reason some PHP is not considering some of the properties as equal (when they are by all appearances) whether or not I directly compare the properties, extract them, or compare their float value:

$lat1 = $coordinates1->latitude; $lat2 = $coordinates2->latitude;
$lon1 = $coordinates1->longitude; $lon2 = $coordinates2->longitude;
if (floatval($lat1) == floatval($lat2)) {
    $myLog->debug("[".floatval($lat1)."] == [".floatval($lat2)."]");
} else {
    $myLog->debug("[".floatval($lat1)."] != [".floatval($lat2)."]");
}
if (floatval($lon1) == floatval($lon2)) {
    $myLog->debug("[".floatval($lon1)."] == [".floatval($lon2)."]");
} else {
    $myLog->debug("[".floatval($lon1)."] != [".floatval($lon2)."]");
}

The results in the debug log are as follows for two different sets of values:

[42.398264] != [42.442251]
[-83.316297] != [-83.33669]
[42.398264] == [42.398264]
[-83.316297] != [-83.316297]

What am I missing?

hakre
  • 193,403
  • 52
  • 435
  • 836
JoBu1324
  • 7,751
  • 6
  • 44
  • 61

3 Answers3

3

The decimal string representations of your floats aren't exactly equal to the floats' true values. It's possible that two floats have different values, but their string representation is the same to some number of significant digits.

Mark Byers
  • 811,555
  • 193
  • 1,581
  • 1,452
2

Don't compare floats for equality. It never quite works the way you want.

It fails on computers for the same reason it fails in decimal. If you represent 1/3 is .33333 then 3 * (1/3) is not equal to 1. If you represent 2/3 as .66667, then 2/3 - 1/3 - 1/3 is not equal to zero and 2 * (1/3) is not equal to (2/3). If you represent 2/3 as .66666, then 2/3 + 1/3 is not equal to 1.

So comparing floats for equality is going to fail in all kinds of horrible and unpredictable ways.

The gory details are explained in What Every Computer Scientist Should Know About Floating-Point Arithmetic.

David Schwartz
  • 179,497
  • 17
  • 214
  • 278
  • That's well and good, but why wouldn't .33333 be considered equal to .33333? I'm not performing any mathematical operations on the numbers – JoBu1324 Oct 27 '11 at 21:28
  • because one could be `.3333333333333333333333333333333` and the other just `.33333333` both rounded differently. – Naftali Oct 27 '11 at 21:34
  • Simply loading them is a mathematical operation because their format in memory is different from their format in the registers where they are operated on and compared. The final result can depend on whether the operation was performed entirely in the floating point unit or whether an intermediate result got written back to memory. – David Schwartz Oct 27 '11 at 21:34
  • 1
    See the warning in the [PHP manual](http://php.net/manual/en/language.types.float.php) which advises you to "never compare floating point numbers for equality". Floating point numbers fundamentally involve rounding and approximation. When you print a floating point number, it is converted into decimal for display. It is binary internally, and the mapping is not 1-to-1. – David Schwartz Oct 27 '11 at 21:36
  • Thanks for the article - I'll up-vote you on that, but Mark's answer was clearer & to the point. – JoBu1324 Oct 27 '11 at 21:37
  • Mark's answer only covers one way it can go wrong. Two floats can compare unequal in one line of code and compare equal in the next despite no intervening code that would appear to change their value. (If they get internally flushed from register to memory and then read back and differed by less than the precision of the in-memory float format.) See [this non-bug](http://stackoverflow.com/questions/1338045/gcc-precision-bug) for an example. – David Schwartz Oct 27 '11 at 21:40
  • True, he only gave one example, but what you're saying is also implied in his answer. On the other hand your answer was off topic, since I wasn't performing even the simplest of calculations on the numbers or using fractions (even if the computer needed to perform calculations in order to display their value). I do appreciate the articles, though, especially the link to the PHP manual! – JoBu1324 Oct 27 '11 at 22:24
  • You convert to floating point and then convert from floating point. Those are both calculations. There was no way to tell from your question how you got the values in the first place (perhaps from calculations, perhaps not) and no way to tell whether the issue was that they should have compared equal but didn't due to calculation/roundoff in generating them or that they shouldn't have compared equal but looked like they should have because of roundoff in printing. – David Schwartz Oct 27 '11 at 22:39
0

my first guess is that the output function (debug) is rounding the values, which internally are stored to much higher precision...

I actually have some code (Python not PHP) that deals with lat/lons as well, and what I ended up doing (this is far from new, and applicable to many situations) is to define a smallish number, say 1e-5 (1/100000) and test for equality by taking the absolutre value of the coords difference. AIf it is less than this small number they are considered equal.

something like

constant TINY = 1e-6

if ( abs(lat1 - lat2) < TINY )
    // they are equal
else
    // they are not equal

HTH

Edit - DEF read the text David linked you to...

Aaron Gage
  • 2,373
  • 1
  • 16
  • 15