Why do I get a different result from two supposedly analogous arithmetic operations?
php, php-5.4
Solution
Firstly, consider that there are many numbers which are rational in base 10, but not in a binary floating point representation. For example, the floating point value of 26.82 is actually `26.8200000000000002842170943040400743484497`
Naturally, if you keep adding this to itself some errors creep in, but up to 15 significant digits you should be fine - add this 1000 times and the sum is actually `26819.9999999997671693563461303710937500000000`
The interesting question though is when we multiple 26.82 by 1000.0 we get `26820.0000000000000000000000000000000000000000` - how does it do that?
The answer there is simply that 26820.0 does have an exact binary representation, and the multiplication operation is smart enough to spot that - even multiplying by 1001.0 and subtracting 26.82 would still get you an exact answer.
Here's a few interesting links
- http://www.h-schmidt.net/FloatConverter/ - this lets you look at single-precision representation of floating point numbers, and can be useful in really understanding floats
- This RFC on PHP rounding is tangentially interesting as it covers some of the issues around rounding floats
- and What Every Computer Scientist Should Know About Floating-Point Arithmetic
Problem
In the following code, why is the multiplication approach not producing rounding errors, while the cumulative addition approach is? ``` function get_value() { return 26.82; } function a($quantity) { $value_excluding_vat = get_value(); $value_including_vat = round($value_excluding_vat * (1 + (20 / 100)),2); $total_nett = 0; $total_gross = 0; for($i=0; $i<$quantity; $i++) { $total_nett += $value_excluding_vat; $total_gross += $value_including_vat; } return array( $total_nett, $total_gross ); } function b($quantity) { $value_excluding_vat = get_value(); $value_including_vat = round($value_excluding_vat * (1 + (20 / 100)),2); return array( $quantity * $value_excluding_vat, $quantity * $value_including_vat ); } $totals = a(1000); print_r($totals); echo $totals[1] - $totals[0]; echo "\n\n"; $totals = b(1000); print_r($totals); echo $totals[1] - $totals[0]; ``` Here's my output: ``` Array ( [0] => 26820 [1] => 32180 ) 5360.0000000005 Array ( [0] => 26820 [1] => 32180 ) 5360 ```