5

I am writing a small script to calculate tax values after deductions. My application has several PHP scripts that provide CRUD functionality. I can add monthly expenses, total billed earnings and then calculate how much corporation tax is owed.

All of the data is stored in a database, however I came across an interesting problem when doing the calculations, I'll simplify the code below, so that it can be reproduced:

$total = 1.0;
$tax = 0.2;
$expenses = 0.05;

echo (($total-$tax) + $expenses); // this echo's 0.85
// (1.0 - 0.2) + 0.05 

if( (($total-$tax) + $expenses) == 0.85 ) {
    echo "totals add up";
}
else {
    echo "accounting error";
}

The idea is that all of the totals are calculated as percentages. 1.0 being 100% of the earnings and the expenses for the month in question is 5% (not really, but for the sake of argument, as my real calculations are a little bit more convoluted), either way this seems to be the simplest that I can get my calculations down to.

So 100% (earnings) - 20% (corporation tax) + 5% (expenses claims) should leave me with a 85% of the total. As shown by the first echo statment.

The if statement however trips up and says "accounting error". What is happening here?

Husman
  • 6,819
  • 9
  • 29
  • 47
  • 1
    It is the usual problem with IEEE754 floating point numbers. Basically you will _never_ have an equality there... To see what I mean, add an `echo (($total-$tax) + $expenses);` line to see what gets there... – ppeterka Mar 13 '13 at 12:06
  • @ppeterka I already have the echo, it prints 0.85 – Husman Mar 13 '13 at 12:09
  • 2
    [What Every Programmer Should Know About Floating Point](http://floating-point-gui.de/) – Barmar Mar 13 '13 at 12:09
  • @Husman ooops, I overlooked that line... What if you rewrite the condition to `( (($total-$tax) + $expenses) - 0.85 ) < 0.000001`? (also, what @akirk suggests is a widely used solution) – ppeterka Mar 13 '13 at 12:13

3 Answers3

5

This is a common problem with floating point calculation. To put it short, some decimal number's can't be described exactly in binary.

The common solution is to stick with integers and calculate everything in cents and only when outputting divide by 100.

akirk
  • 6,757
  • 2
  • 34
  • 57
1

take a look at the manual for comparing floating points: http://php.net/manual/en/language.types.float.php

djjjuk
  • 368
  • 1
  • 2
  • 14
1

You may want to investigate PHP's BCMath extension. I found the reference by searching this site and locating the question here.

Community
  • 1
  • 1
Gord Thompson
  • 116,920
  • 32
  • 215
  • 418