3

In one of my wordpress theme function, this code is being used to calculate total billable amount: ceil(($cost * (1 + ($charges / 100))) * 100);

There is a little miscalculation happening for below scenario.

Scenario:

$charges = 9;
$cost = 100;

echo ceil(($cost * (1 + ($charges / 100))) * 100);

The above code outputs 10901 whereas it should be 10900.

It works fine for other scenarios like:

$charges = 4;
$cost = 90.7;

echo ceil(($cost * (1 + ($charges / 100))) * 100); 
//outputs 9433, which is fine because manual calculation results 9432.8

Question:

  1. Why is that happening?
  2. How can I prevent that?
  3. Any alternate function to round to next nearest integer? (only if amount is floating value)
7
  • @Phil none of those answer helped, First, it's not a math problem. Second, it does work almost fine but causes error only for specific numbers like ending with 00. Kindly unmark the question or share helpful resource. Thanks Commented Jul 31, 2018 at 4:53
  • 3
    It seems you really are facing problems caused by numerical accuracy. Although I found that simplifying your equation does help: ceil($cost*(100+$charge));, see here: rextester.com/QQEYOS31143 Commented Jul 31, 2018 at 5:42
  • thanks @cars10m, there's another issue. Your equation works fine on rextester.com but on my server, it has same issue. :( I'm running 5.6.32 Commented Jul 31, 2018 at 5:51
  • @Alena it most certainly is a floating point arithmetic problem. For example, 100 * (1 + (9 / 100)) comes out as 109.00000000000001. This is why ceil() appears to be rounding up. Please read both posts I've linked at the top of your question Commented Jul 31, 2018 at 6:21
  • 1
    You could try using round($cost*(100+$charge),6) to round to the closest number with 6 digits after the decimal point before using your ceil() function. However, round() is reported to be buggy in some cases. Otherwise you can also do a detour by creating a rounded string, like sprintf('%0.6f',$cost*(100+$charge)) and then continue to do math with it (it will implicitly be converted back to float). In both cases the ceil() function will be applied afterwards to turn the values into "the next larger integer". Commented Jul 31, 2018 at 8:06

1 Answer 1

2

The problem is that you are applying ceil to the outer expression. Try to rewrite it as:

$charges = 9;
$cost = 100;

echo ($cost + ceil($cost * $charges / 100)) * 100;

This outputs 10900 as expected.

UPDATE

As @cars10m suggested, simplifying the expression does help:

echo ceil($cost * 100  + $cost * $charges);

UPDATE 2

You can also use BCMath library to do precise math:

bcscale(6);
echo ceil(bcadd(bcmul($cost, 100), bcmul($cost, $charges)));
Sign up to request clarification or add additional context in comments.

6 Comments

thanks @Olim Saidov, but that does not work fine for other scenario. Try it with these values $charges = 4; $cost = 90.7; It results 9470 whereas it should be 9433
Can you explain the calculation logic? Why it should be 9433?
1) charges/100 = 0.04 2) 0.04 + 1 = 1.04 3) 1.04 * cost = 94.328 4) 94.328 * 100 = 9432.8 5) rounding to next integer will make it 9433
the new solution does not work on my server. I'm running 5.6.32
using @cars10m method, the rextester gives correct result but on my server, it gives same result like my older expression. Maybe that's because of php version etc. However, the bcscale seems working. I will perform different tests and get back soon. Thanks for the help! :)
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.