https://i.imgur.com/2C4sVIG.png
Can someone please explain this to me? It doesn’t happen for most other numbers such as 9.33, 119.33 – why the hell is this? It’s clearly got something to do with floating points but if so I’d have expected other numbers to act the same way.
I’ll try:
(round(19.33, 2) * 100)
is represented as a double in memory, probably as 1932,99999.
You cast that as integer, meaning the decimals get cut off.
You end up with 1932.
Try this:
(int)round(19.33 * 100);
Should yield 1933. What gets casted is still a double but you do not mess around with the decimal part of the value.
Why this does not happen to all other values? My guess is that this is due to the specifics of their representation as non-int in memory. You’d have to dig way deeper than my explanation attempt though.
edit:removed superfluous brackets
It doesn’t always happen because (int) rounds towards zero afaik. So sometimes the floating point represents a number that is just slightly above what you want to represent. Compare:
That makes a lot of sense, thanks!
https://3v4l.org/UFIFe
Output for 8.0.0
float(1932.9999999999998)
Output for 7.3.0 – 7.3.26, 7.4.0 – 7.4.14
float(1933)
I assume you understand the basic premise about floating point numbers not being 100% precise. As to why this particular sum has changed from PHP 7 to PHP 8, I’ve not the foggiest. Not sure if it would be considered a bug either, since it is a generally-known problem with floating point numbers.
If you do want precision, avoid decimals. For instance, if your app deals with monetary amounts, store cents or pence in the database, not dollars or pounds. Divide by 100 only when displaying figures to the end user. Use the whole-integer amounts when computing tax percentages or adding values together, etc.
Makes sense though I wonder why it changed between versions
It’s a matter of printing. I get the following on 7.3:
More resources:
https://www.php.net/manual/en/language.types.float.php (scroll down to the Warning)
https://floating-point-gui.de/
TL;DR: don’t use floats if you ever need to rely on rounding. Use an arbitrary-precision arithmetic library such as brick/math. Example:
Disclaimer: I’m the author.
Do you have a real-word example of a bug that was solved by switching away from the usual 64-bit or 80-bit floats?
I have made a lot of engineering and finance software. It had bugs relatied to rounding, but floating point maths was never the cause. There was always enough precision available.
Because the Float value of “19.33” is internally handled as “19.3299999237060546875” and even if you “round” that it’s still “19.3299999237060546875” as “19.33” is not representable as a float data type. So then you multiple that value by 100, it becomes “1932.99999237060546875” and the typecasting then reduces that number to “1932”.
This is not even a bug but simply unpredictable in classic IEEE-754 floating point math…
—
There are multiple ways to solve that specific issue, you could use GMP or BCMath Extensions for this.
Basically with floats, the larger the number the less precise numbers the smaller components of it get because it is moving the limited precision to representation of the larger digits. Not every integer can be represented exactly in a float, which I think most people do not realize.