0

I got a problem with this script

 $total = 0;
    $expected_total =  1111;
    $i = [85.46,85.46,85.46,85.46,85.46,85.46,85.46,85.46,85.46,85.46,85.46,85.46,85.48];
    foreach ($i as $item) { $total += $item; }
    if($total != $expected_total) {
        echo json_encode([$total,$expected_total]);
    }

The problem is that at the end of the sum the $total should be equal to the $expected_total. I thought about different number types, I printed the type of the two vars and I was right, one was double and the other was integer, so I converted the integer to double

$expected_total = 1111.00;

but the result is still the same.

The only solution that I could find was comparing the rappresentation of the two numbers, casting them to a string.

 if((string)$total != (string)$expected_total) {
        echo json_encode([$total,$expected_total]);
    }

But obviously this is kind of a hack.

Have you ever had a similar problem? How have you solved it?

PHP version : 5.5.9 Many Thanks

  • 2
    http://stackoverflow.com/questions/3148937/compare-floats-in-php and there's also a section of the [PHP docs](http://php.net/manual/en/language.types.float.php) dedicated to the topic, and showing how the comparison should be done (and it doesn't involve strings) – Mark Baker Aug 14 '15 at 08:43
  • 1
    This is not only PHP problem. It is about representation of floating point numbers in memory. – Robert Aug 14 '15 at 08:45
  • The easiest way out is to store all the prices (it's prices, right?) in cents, so that you'll add and compare integers only. – raina77ow Aug 14 '15 at 08:47

3 Answers3

3

This is not only PHP problem. It is about representation of floating point numbers in memory. Floating numbers has limited precision. PHP uses IEEE 754. Read carefully the manual page and you will understand.

You can find in manual code snippet of how to do it.

$a = 1.23456789;
$b = 1.23456780;
$epsilon = 0.00001; //very small number

if(abs($a-$b) < $epsilon) {
    echo "true";
}
Robert
  • 19,800
  • 5
  • 55
  • 85
0

If you want to check them as integers you could round both values. You should change the if-statement to the following:

if(round($total) != round($expected_total)) {
  echo json_encode([$total,$expected_total]);
}

If you do it like this you will compare the rounded values, which will be the same.

Jeroen de Jong
  • 337
  • 1
  • 6
0

There is a big red label in the PHP Manual, that says:

Floating point numbers have limited precision…

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

In this specific case, you could add the floating numbers using bcadd() and compare the totals using bccomp(). Both functions are provided by the BC Math extension:

foreach ($i as $item) { 
    $total = bcadd((string) $total, (string) $item, 2);
}
if (bccomp((string) $total, (string) $expected_total, 2) == 0) {
    echo json_encode([$total,$expected_total]);
}
UrsaDK
  • 854
  • 13
  • 27