8

When I'm executing the following code in PHP (v5.5.9) something unexpected happens:

$valueAsCents = 54780 / 100 * 100;
var_dump($valueAsCents);
var_dump((int) $valueAsCents);

This returns

float 54780
int 54779

So apparently the float value with no decimals, is not equal to the int value. Any ideas of what's going on here?

10
  • echo serialize($valueAsCents); Commented Sep 15, 2015 at 15:14
  • 3
    Looks like a floating point error. The actual value of $valueAsCents is 54779.999999999993. It appears that as a float, it is being rounded (not sure why, however) and when cast as an int, it is being floored. Or so it seems, anyway.
    – Josh
    Commented Sep 15, 2015 at 15:17
  • 2
    If you work with floats and expect any sort of rational accuracy, you're doing it wrong. You can simply never be sure when something will round just a tiny bit in the wrong direction.
    – deceze
    Commented Sep 15, 2015 at 15:17
  • 1
    Floats are stored binary, which has a limited resolution. It's rounded off to a certain extend. Dividing by 100 and then multiplying the result by 100 can therefor come up with another number.
    – patrick
    Commented Sep 15, 2015 at 15:19
  • Well, I'm glad I'm not going crazy ;-) But why is $valueAsCents 54779.999999999993? I understand it is then floored to 54779.
    – DerLola
    Commented Sep 15, 2015 at 15:23

2 Answers 2

6

When you divide $valueAsCents = 54780 / 100 then it becomes a float which is not always accurate in digital form because of the way they are stored. In my tests I got
547.7999999999999545252649113535881042480468750000

When multiplied by 100 this is would be
54779.9999999999927240423858165740966796870000

When PHP casts to int, it always rounds down.
When converting from float to integer, the number will be rounded towards zero.

This is why the int value is 54779

Additionally, the PHP manual for float type also includes a hint that floating point numbers may not do what you expect.

Additionally, rational numbers that are exactly representable as floating point numbers in base 10, like 0.1 or 0.7, do not have an exact representation as floating point numbers in base 2, which is used internally, no matter the size of the mantissa. Hence, they cannot be converted into their internal binary counterparts without a small loss of precision. This can lead to confusing results: for example, floor((0.1+0.7)*10) will usually return 7 instead of the expected 8, since the internal representation will be something like 7.9999999999999991118....

3
  • 5
    Thanks for this clear explanation. In case anyone's wondering, the workaround for this is to first round the outcome, before casting to int.
    – DerLola
    Commented Sep 15, 2015 at 16:07
  • @DerLola Are there any edges cases with using round() ?
    – manask322
    Commented Aug 4, 2021 at 9:17
  • @manask322 Not sure what you mean. If you are okay with losing the decimals, you can just do (int) round($a / $b). Otherwise you can use php.net/manual/en/function.bcdiv
    – DerLola
    Commented Aug 5, 2021 at 10:05
1

In this website (https://floating-point-gui.de/) there are solutions for floating-point issues in various languages, and among them PHP https://floating-point-gui.de/languages/php/.

1
  • 1
    Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.
    – Community Bot
    Commented Mar 13, 2024 at 17:20

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.