14

I'm working on a few report output scripts that need to do some rudimentary calculations on some money values.

I am aware of the limitations of floating point arithmetic for this purpose, however the input values are all in a decimal format, so if I use the arithmetic operators on them PHP will cast them to floats.

So what is the best way to handle the numbers? Should I use BCMath? Is there something akin to Decimal in .NET? Or is it safe to use the arithmetic operators if I cast back to int?

Demi
  • 6,147
  • 7
  • 36
  • 38
Shabbyrobe
  • 12,298
  • 15
  • 60
  • 87
  • It's the reason why Cobol is still heavily used in financial. You can easily works with decimals numbers. – Luc M May 23 '09 at 03:09

3 Answers3

14

If you use BCMath all values will be stored in strings and passed to the functions as strings, just as the result will be a string. So, you needn't do any casting but ensure that the number given to the function is a value numeric value. Personally, if the math requires a high amount of precision on the decimal side then use BCMath.

Robert K
  • 30,064
  • 12
  • 61
  • 79
13

Don't work in Dollars ($1.54), work in cents: (154c). Unless you need to be performing tasks where fractions of a cent are important, then you'll be working with integers and all is well. If you are interested in tenths of a cent, then just multiply everything by ten!

nickf
  • 537,072
  • 198
  • 649
  • 721
  • To convert a currency, banks provide us rates with 8 decimals. – Luc M May 23 '09 at 03:07
  • What about for display? It still needs to be shown as $1.54. Is there any situation with a floating point number where I'll end up with 1.53 or 1.55? – Shabbyrobe May 23 '09 at 03:22
  • just use a wrapper function: displayMoney($value){echo '$' . $value * 100;} – SeanDowney May 23 '09 at 07:27
  • is php really this bad at floats? I mean really, this seems sad – SeanDowney May 23 '09 at 07:28
  • 7
    Sean: No, the point is that you shouldn't use floats, regardless of the programming language. They are imprecise. – troelskn May 23 '09 at 11:02
  • 14
    A problem I find in this solution, is when working with taxes or discounts. You still need to multiply by a float. How would you solve that problem when working with ints? – Lumbendil Apr 20 '12 at 08:52
-4

If you're working with reasonable amounts (to a "normal person"), using floating point is not likely to be a problem, especially if you're just adding and subtracting amounts, rather than doing, say, interest calculations.

If you're looking for a quick fix, switching to integer is not likely to help you; you still have to deal with overflow. (Someone edited here to mention that if PHP encounters a number beyond the bounds of the integer type, it will be interpreted as a float instead. But then you're back to your original problem of using floating point!) Arbitrary length integers (GMP, referenced from that page) can help.)

Basically, doing this in a half-assed way works in most cases, and is cheap; doing it correctly is a serious pain. My suggestion is to look at this as a business problem: if someone complains about missing a cent, give them a dollar, and don't bother starting on a real solution until you're going to save more dollars by doing that.

cjs
  • 25,752
  • 9
  • 89
  • 101
  • 2
    That is incorrect. Floating points are incapable of representing all base-10 numbers. Even with small numbers, and simple addition/subtraction, you can get incorrect results. – troelskn May 23 '09 at 11:04
  • 1
    Rounded floating point numbers can represent all numbers between 0.01 and 1.00 (note significant digits there) just fine. – cjs May 24 '09 at 04:23
  • 1
    @cjs, I worked with a team that used floats to implement a wallet. I was implementing a payment service that became pretty well-known. They were only summing and subtracting values. They found out they were losing money after several operations. And that's when I learned they were using floats. Try to run this: `printf('%.20f', 0.1);`. Probably you'll get something like '0.10000000000000000555', which is not exactly 0.1. If you sum 0.1 nine-hundred times, and “echo” the result (`echo $sum;`), you may get something like “89.999999999999”. Your “solution” to a business problem is a problem. – Pedro Amaral Couto Dec 29 '22 at 02:32
  • @PedroAmaralCouto To two significant digits, that number is exactly correct. Put it into a rounding function and you'll see. ¶ Yes, it's true that innumerate programmers can get caught by things like this, where they incorrectly interpret the output to have much more precision than their inputs. But someone so innumerate is quite likely to get caught by other things (such as the overflow I mentioned) should they use "int" representations. The lesson here: int or float, you can't trust numerical calculations made by those who do not understand what they're doing. – cjs Dec 29 '22 at 10:57