0

How can i compare 2 floats the right way?

Background: I got a packing tool that packs items into containers.

Whenever i get an object (item, container) into the tool i do cast them to (float).

Everything worked fine until i wrote some (more) tests.

The comparison:

$currentContainerHeight + $itemHeight <= $maxContainerHeight

Testoutput:

// actual expected coparison:
1.2345 <= 1.2345 

// left  === $currentContainerHeight + $itemHeight
// right === $maxContainerHeight

right           : 1.2345000000000002
left 1          : 1.1110500000000003
left 2          : 0.12345
left sum        : 1.2345000000000004

// "left 1" + "left 2"  <=  right
1.2345000000000004      <=  1.2345000000000002

Before i go thought all the code and cast the values to (double) i wanted to ask if this would make sense?

Or how do you handle such a situation the best?


After reading and testing i found out that the most efficient way is to use a leeway|threshold|epsilon.

This may not work the best for everybody but in my case it makes the most sense.

PHP 7.2 comes with PHP_FLOAT_EPSILON.

I wanna know if something i will fill up has still space for something i want to put in.

So:

(
    $currentContainerHeight + $itemHeight - PHP_FLOAT_EPSILON
) <= $maxContainerHeight

time checks:

$container >= $item - PHP_FLOAT_EPSILON                             // 0.263153 at 10000000 loops
$container > $item or abs($container - $item) < PHP_FLOAT_EPSILON   // 0.538085 at 10000000 loops
bccomp(a, b, 4) >= 0                                                // 7.643388 at 10000000 loops
sprintf('%f', $container) >= sprintf('%f', $item)                   // 6.767077 at 10000000 loops
cottton
  • 1,522
  • 14
  • 29
  • PHP default float precision stops at 16 (that's why 0.2 + 0.1 is 0.3 in this language by default). You can change it in your `php.ini` file. But in every language you should round your outputs. In PHP you can do it by using `round(number, precision)`. – Jax-p Aug 09 '19 at 18:45
  • But i should not round i guess. When i round f.e. 200 items then i may get a "gap" of 200 x n. – cottton Aug 09 '19 at 18:50
  • 1
    You add 200 items together and then round just before the comparison. But rounding is not needed, you can have an threshold. `1.2345000000000004 <= 1.2345000000000002 + $threshold` where the threshold is an insignificant difference like `0.000000001`. – KIKO Software Aug 09 '19 at 18:52
  • Thanks. I see this will run into duplicate. Just for others having the same problem: see https://stackoverflow.com/questions/3148937/compare-floats-in-php and https://www.php.net/manual/en/language.types.float.php#language.types.float.comparison – cottton Aug 09 '19 at 19:04
  • @KIKOSoftware After some research and testing i think this is the fastest way. Im using PHP `PHP_FLOAT_EPSILON` now. Thanks again. – cottton Aug 09 '19 at 23:13
  • `PHP_FLOAT_EPSILON` might be too small for some of your use cases. For instance if you add a lot of items together, of which some are slightly larger than intended, you might easily go over the `PHP_FLOAT_EPSILON` threshold. In my opinion the threshold value should be insignificant for packing, but big compared to `PHP_FLOAT_EPSILON`. – KIKO Software Aug 10 '19 at 07:09
  • True. I use the `PHP_FLOAT_EPSILON` on every comparison. So on every item. Lets say i have an item that fits exactly into one space. It actually would fit but it does not due to the float problem. With the `PHP_FLOAT_EPSILON` leeway it fits. (i guess not perfect for sience but enough for bin packing). – cottton Aug 10 '19 at 14:52

0 Answers0