3
for($i=0;$i<10000;$i++) {
    $random=random_int(100,1000);
    $div = random_int(1,100);
    $rant_zoom = $random/$div;
    $rant_zoom = $rant_zoom*$div;
    if ($random != $rant_zoom) {
        var_dump($random,$rant_zoom);
        echo "-----------\r\n";
    }
}

example for output

It is possible for floating point numbers to be inaccurate and not equal to the original value, but the results printed in var_dump show the same value, why is this?

longbin
  • 31
  • 1
  • Does this answer your question? [Compare floats in php](https://stackoverflow.com/questions/3148937/compare-floats-in-php) – Justinas Mar 18 '21 at 11:52
  • Yes, it's possible, because some of float numbers has infinite decimal part and that is not possible to place inside finite memory place – Justinas Mar 18 '21 at 11:53

1 Answers1

0

I can't explain the underlying "magic" with the bits, but I'm pretty sure this is related to how fixed and float numbers are represented and cast.

A few things to note in your code:

  • the vars $div and $random are int and stay int
  • in you if you are comparing an int to a float with != so the values are casted before comparing them

While researching the answer I also came across this SO post, which shows that var_dump is secretly rounding the numbers

This is my version of your code, I wanted to try multiple approaches:

for($i=0; $i<100; $i++) {
    $original = mt_rand(100, 1000);
    $div      = mt_rand(1,100);

    $internal_part = $original / $div;
    $internal      = $internal_part * $div;

    $bcmath_part = bcdiv($original, $div, 50);
    $bcmath      = bcmul($bcmath_part, $div, 50);

    $diff_1 = $original - $internal;
    $diff_2 = bcsub($original, $bcmath, 50);

    var_dump($original, $internal, $div);

    echo "<p>Full scale results:</p>";

    echo sprintf("Internal: %s <br>", rtrim(number_format($internal, 100),0));
    echo sprintf("Bcmath: %s <br>", rtrim(number_format($bcmath, 100),0));

    echo "<p>both as Float</p>";

    echo sprintf("Division: <code>%f</code> ", $internal_part);

    echo sprintf("Internal: <code>%f</code> != <code>%f</code> Diff1: %f<br>", (float)$original, $internal, $diff_1);

    echo sprintf("Division: <code>%f</code> ", $bcmath_part);
    echo sprintf("Bcmath: <code>%f</code> != <code>%f</code> Diff2: %f <br>", (float)$original, $bcmath, $diff_2);

    echo "<p>both as INT</p>";

    echo sprintf("Internal: <code>%d</code> != <code>%d</code><br>", $original, (int)$internal);
    echo sprintf("Bcmath: <code>%d</code> != <code>%d</code>", $original, (int)$bcmath);

    echo "<hr>";
}

In the "Full scale" print, you should see that the internal division (default) results are just very-very close to an integer, but they aren't:

Just one example of 740 / 43 * 43:

Simple division and multiplication : 739.9999999999998863131622783839702606201171875
using Bcmath: 740

I think var_dump just gives up after a number of nines and calls it an integer, while the actual value is still a float.

Conclusion: use bcmath or something precise in these cases - especially in finance

zedling
  • 638
  • 8
  • 28