views:

45

answers:

3

I was reading that you have to be careful with floating point comparisons and calculations in PHP. I'm not an expert at PHP so I thought I would ask the community what is the best way to do accurate decimal calculations in PHP.

I'm developing a timecard application where it calculates all the time entered for the week. Anything over 40 hours is considered overtime. I then want to distribute that overtime back across each time entry based on the percentage that entry is of the total time.

Example:

Monday = 10 hours
Total Time = 50 hours
Total Overtime = 10 hours

Percent of Total time (0.2) = Monday (10 hours) / Total Time (50 hours)
Overtime to Allocate to Monday (2 hours) = Total Overtime (10 hours) * Percent of Total Time (0.2)

Now make a new entry for Monday with 2 hours of overtime and subtract 2 hours from original entry.

New Entries:
Monday (8 hours) = Monday - Overtime to Allocate to Monday (2 hours)
Monday Overtime = 2 hours

Things don't seem to bad when using integers as your original entries but what will happen when I start using floats and have multiple time entries per day? Each time entry will have part of the total overtime allocated to it but I need to make sure not to allocate more overtime then is available and I also need to make sure the total time for each day and for the whole timecard is the same as originally entered. I read comparing floats in php are not safe. How should I do the calculation. Also, the final entered numbers have to be rounded to 2 decimal points (.##)

A: 

I think you'd best look at the BCMath lib/functions: http://php.net/manual/en/book.bc.php

Dennis Haarbrink
A: 

Read The Floating-Point Guide to understand why exactly you have to be careful with mixing floating-point numbers and the concept of "decimals" (which has absolutely nothing to do with PHP specifically).

It also has a cheat-sheet for PHP.

Michael Borgwardt
+1  A: 

You could use fixed point maths. So for example if your values need to be accurate to one cent, then use one cent as your unit and then you can use the value 1234 to represent 12.34, and so on.

You'll still find that some operatings including division may return a floating point value. You'll just have to decide what to do with the remainder anyway. For instance if you have 1.04 and you divide that by 5, should the result be 0.21 (rounded up or nearest) or 0.20 (rounded down) etc. Then to convert back to integer using round, ceil or floor.

thomasrutter
In the OP's case, this equates to calculating in integer minutes or even seconds, rathe rthan in floating point hours
Mark Baker