44

I need to add, multiply and compare currency values in PHP and need to be sure that it is exact down to a single cent.

One way is to store everything in float, use round before and after each operation and mind machine epsilon when comparing for equality. Quite cumbersome imho.

Another way is to store the whole thing as cents in integer data types, plus remember to convert back and forth anytime I work with the database (mysql, where I use the decimal data type). Inelegant, and many error pitfalls, imho.

Another way is to invent my own "datatype", store all values in strings ("34.12") and create my own mathematical replacement functions. These function would convert the value to integers internally, do the calculation and output the result again a strings. Suprisingly complicated, imho.

My question: what is the best practice for working with currency values in PHP? Thanks!

CruftyCraft
  • 751
  • 2
  • 8
  • 15

5 Answers5

40

As of MySQL v5.0.3, MySQLs DECIMAL datatype stores an exact decimal number, i.e. not an inaccurate floating point representation.

To correctly manipulate precision numbers in PHP use the arbitrary precision math functions. Internally this library manipulates text strings.

Currency is intended to be stored as a decimal number, you can get units of money smaller than cents. You should only round any values when displaying the figures, not during manipulation or storage.

Randy the Dev
  • 25,810
  • 6
  • 43
  • 54
  • 1
    I'm sorry but I don't understand you. There is no decimal data type in php. If you meant float: you cannot reliably test for equality using them. – CruftyCraft Sep 29 '10 at 07:25
  • 12
    PHP has a set of functions designed specifically for the manipulation and comparison of precision numbers: http://www.php.net/manual/en/ref.bc.php – Randy the Dev Sep 29 '10 at 07:39
6

Update 2016:

I absolutely do not advise storing 'money' as integers anymore. The only true answer is Andrew Dunn's: Use Mysql's DECIMAL type and encapsulate php's bc_* functions in a Currency class.


I will keep my outdated answer here for completeness sake

You should always work with integers. Store the values as ints in your database, use ints to do calculations and when, and only when, you want to print it, convert it to some readable format.

This way you completeley circumvent floating point problems, which, generally, is a big problem when working with money ;)

Edit: One thing worth mentioning: You need to think about precision before you start working this way. As others have pointed out, you might need to work with values smaller than a cent, so you need to do your multiplication/division accordingly. (ie currency * 1000 for a .001 precision)

miken32
  • 42,008
  • 16
  • 111
  • 154
Dennis Haarbrink
  • 3,738
  • 1
  • 27
  • 54
  • 3
    MySQL and others have the DECIMAL / NUMERIC format which is designed specifically to circumvent floating point problems and give exact results. – Tatu Ulmanen Sep 29 '10 at 07:26
  • 3
    I assumed irony, so I didn't vote you down. Please confirm I was right :) – thomasmalt Sep 29 '10 at 07:28
  • 1
    @Tatu Ulmanen: Sure, Mysql does. But if you then go about and do something like equality testing in php, you might get unexpected results. – Dennis Haarbrink Sep 29 '10 at 07:28
  • 1
    +1 assuming you are not working with values smaller than 1ct. Just use integers in both the database and php and convert when displaying like Dennis is suggesting. – Mischa Sep 29 '10 at 07:35
0

Let me answer this myself. Best practice would be to create a "money amount" class. This class stores the amount internally as cents in integers, and offers getters, setters, math functions like add, subtract and compare. Much cleaner.

CruftyCraft
  • 751
  • 2
  • 8
  • 15
  • 6
    This is not, quite simply, best practice and very bad advice. Please, by all means, encapsulate functions that work with arbitrary precision maths functions, but please do not follow the advice above. The best answer is by Andrew Dunn. – James Dunne Jul 10 '15 at 13:42
0

Keep the values as cents and use integers. Write classes or helper functions to encapsulate displaying in the UI and handling values in your database queries. If you use float, there is too much risk you'll lose a cent somewhere.

Matthew Smith
  • 6,165
  • 6
  • 34
  • 35
0

I tried multiplying the floats by 100 before all mathematical operations, then dividing by 100 before displaying the result. (It worked!)

Mathematically, that's "converting to integers" like everyone else is suggesting, but the code looks a lot more elegant without any conversion functions. Also, to avoid confusion down the line, I just added "Cents" to the end of any value names.

From the machine side, it's probably faster to use integers instead of float, but writing human-readable code I prefer my solution. (Your mileage may vary, but it worked in my application.)