Mark Rogers wrote:
Can anyone explain this code's output?
<?php $x = (15/100) * 10.7; $y = 1.605; var_dump($x, $y, round($x,2), round($y,2),round(1.605,2)); ?>
Result: float(1.605) float(1.605) float(1.6) float(1.61) float(1.61)
In other words there is a difference between the values of $x and $y where they should both be 1.605 (and var_dump shows them both to be 1.605).
I assume this is because $x isn't really 1.605, but something very close to (and slightly below) 1.605 due to a rounding issue, so rounding to 2 decimal places causes it to round down not up. But I'm not sure how to change the code to give the correct results?
(15/100 you may notice as VAT; this is a VAT calculation, therefore I'm kinda duty bound to get the right result!)
Hi, I'm coming to this discussion a bit late but the following may help.
It's an old COBOL dodge that is still used in many systems to fix rounding problems in currency calculations.
Do no rounding at all during the calculation but use the best precision you can. Then just before the final stage where you round the result add half a penny.
So your code above would become:
$x = ((15/100) * 10.7) + 0.005); $y = 1.605; var_dump($x, $y, round($x,2), round($y,2),round(1.605,2));
which should give: float(1.61) float(1.605) float(1.61) float(1.61) float(1.61)
It may amuse you to see the calculation of (15/100) * 10.7 done in 32 bit binary where the rounding issue becomes obvious.
(15/100)*10.7 -> (1111 / 110010) * 1010.1001100110011001100110011001 because of binary rounding this becomes (15/100)*10.699951171875 and (0.15) -> 0.0010011001100110011001100110011 which is 0.14996337890625 0.0010011001100110011001100110011 * 1010.1001100110011001100110011001 (I'm not going to do that one! but it gives a decimal result of: 1.60460083186626434326
note that both 0.15 and 10.7 can not be represented in binary as they create recurring values after the binary point.
and now you can see that $x = 1.60460083186626434326 so round($x,2) is correctly 1.6
using the half penny fixup would give $x = 1.60960083186626434326 so round($x,2) would now be 1.61 as required.
You know, I never thought my time spent debugging the Spectrum's 32bit floating point calculator would ever come in handy again. Or for that matter my time spent writing COBOL systems. :-)
In case you're wondering I am older than COBOL.
I think I need to go lie down now :-)
Nev