3

I'm calculating fees (monetary) for something and PHP is behaving strangely when the amount is supposed to be 0.00. It's giving the final result as a floating number, not 0 which it should be.

In My database I have the following table

id   |  transaction_total   |    charge_fees   |  deposit_fee   |   amount_to_customer   |   fees_minus_total_difference

So when I go to check to make sure that the fees + amount paid - total = 0.00

(96.54 + .25 + 3.20 - 99.99) = 1.4210854715202E-14

Why is the result a floating number and not actually zero? The numbers are originally more decimal places, but I used number_format to put it into 2 places. Ex, charge fees may actually be 3.19987

number_format(3.19971,2,'.','')  //equals 3.20

When I save this in my database it displays as 3.20. When I use it in the computation for the total/fee check, the result is not zero, albeit close.

user1443519
  • 591
  • 1
  • 12
  • 26
  • 1
    http://en.wikipedia.org/wiki/Floating_point#Representable_numbers.2C_conversion_and_rounding – deceze Nov 25 '13 at 19:42
  • 1
    Never use floating points for *monetary values*, unless you don't mind losing small amounts of money (or gaining some, maybe). Either calculate in cents or with an arbitrary precision library like bcmath. – deceze Nov 25 '13 at 19:46
  • possible duplicate of [PHP Math Precision](http://stackoverflow.com/questions/3726721/php-math-precision) – deceze Nov 25 '13 at 19:47

2 Answers2

4

Computers store numbers in binary, so when you try to represent a decimal number you may lose precision. That is what is happening here.

Some languages have exact-precision types, but PHP, unfortunately, does not. It does provide you with both BC Math and GMP, though. Using those here seems overkill, though. Using BC, you could do this though:

bcsub(bcadd(bcadd('96.54','0.25',2),'3.20',2),'99.99',2) = 0

Notice that you have to specify the number of decimal points (2) here also.

Generally, using floats and double for finances is frowned upon, but with PHP it does seem like the simpler option.I suggest you just round your number using round() to the number of decimal places of your input.

round(96.54 + .25 + 3.20 - 99.99,2) = 0
kba
  • 19,333
  • 5
  • 62
  • 89
  • You need to feed numbers as strings to the bc functions. Just writing `0.12345` in your source code has already made you lose precision. It should be `bcadd('1.234', '5.678')`. – deceze Nov 25 '13 at 20:17
2

I would recommend not using number_format() other than for displaying strings. Use round() instead.

Your issue is with floating points. PHP has more accurate math functions you can look at like bcadd, bcmul, bcdiv, etc...

http://php.net/manual/en/book.bc.php

Pitchinnate
  • 7,517
  • 1
  • 20
  • 37