fmod(floatval("314.6"), floatval("1.3"))
=> 1.1990408665952E-14
I understand more or less the underlying problem of representing the numbers in binary form and something with IEEE-754. But: How would I get a result that is usable in pratice?
Example what I want to achieve: A merchant can define the price P of his product, but the price has to be a multiple of x:
if (fmod(P, x) != 0) { echo "price must be a multiple of " . x; }
It would be so simple, but the approach fails whenever I get something like 1234E-18 as return value of fmod()
.
What to do in real life to check the price interval easily without using custom code?
This or similar questions have been around, but all I can find are explanations why fmod()
behaves like it does. Never an answer how to solve this real-life problem...
The problem here is that 314.6
is exactly 1.3 * 242
so floating point remainder is zero but you get 0.00000000000001199041
due to the IEEE 754 inaccuracies you're well aware of.
But don't forget one of the rules of floating point maths: you can't compare floats for equality carelessly. If your arguments have one decimal position you don't need 14-position accuracy in your results. You have prices: how many decimals make sense in your currency? If you were using e.g. euros you're unlikely to use more than two (cents) and 1.1990408665952E-14
is zero to all effects:
var_dump(round(1.1990408665952E-14, 2));
double(0)
Some people recommend doing all price maths using integers (e.g. cents instead of euros) to avoid rounding errors but most real life issues come from very specific errors:
if ($foo == $bar)
).… and integers don't prevent all rounding errors anyway (e.g. tax calculations on individual items not matching calculations on invoice totals).