Why am I getting a different result from two supposedly similar arithmetic operations?

In the following code, why does the multiplication method not create rounding errors, whereas the total addition method is equal?

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 is my conclusion:

 Array ( [0] => 26820 [1] => 32180 ) 5360.0000000005 Array ( [0] => 26820 [1] => 32180 ) 5360 
+6
source share
3 answers

First, keep in mind that there are many numbers that are rational in base 10, but not in binary floating-point representations. For example, a floating point value of 26.82 is actually 26.8200000000000002842170943040400743484497

Naturally, if you continue to add this to yourself, some errors creep in, but up to 15 significant digits you should be fine - add this 1000 times, and the amount is actually 26819.9999999997671693563461303710937500000000

An interesting question, though, when we multiply 26.82 by 1000.0, we get 26820.0000000000000000000000000000000000000000 - how does he do it?

The answer simply is that 26820.0 has an exact binary representation, and the multiplication operation is smart enough to notice that even multiplying by 1001.0 and subtracting 26.82 will still give you an exact answer.

Here are some interesting links.

+6
source

The problem may be in the machine representation of float values (see warning )

eg. 21470.73 can be 21470.729999..9994561 or 21470.73000 ... 0001231, depending on how they were calculated.

Try rounding temporary values ​​like gross_total_so_far and nett_total_so_far before calculating $total

+3
source

I can not guess the problem of representing and subtracting a floating point number But,

When you subtract these values ​​without a round, you get the result ** 3578.455

And when you round it to two decimal places, round it with 3578.46 .

So php has a solution with this problem.

 PHP_ROUND_HALF_UP Round val up to precision decimal places away from zero, when it is half way there. Making 1.5 into 2 and -1.5 into -2. PHP_ROUND_HALF_DOWN Round val down to precision decimal places towards zero, when it is half way there. Making 1.5 into 1 and -1.5 into -1. PHP_ROUND_HALF_EVEN Round val to precision decimal places towards the next even value. PHP_ROUND_HALF_ODD Round val to precision decimal places towards the next odd value. 

These constants are provided with a round function as

 echo round(100.675, 2, PHP_ROUND_HALF_UP); // 100.68 echo round(100.675, 2, PHP_ROUND_HALF_DOWN); // 100.67 

So PHP_ROUND_HALF_DOWN Will be useful in your case

+1
source

Source: https://habr.com/ru/post/951396/


All Articles