Another one of those oft-ignored rules for safe programming. You have a program that works in dollars and cents, say. The most natural mapping of the world to your program's variables is to store dollars in real numbers. So it would seem you'd want stuff like this:
double price = 0.10; /* Price is 10 cents */
Take a closer look at what happened here: we didn't store 0.1 in a real number
, we stored it in a floating point number
! And floating point is not a representation of real numbers (although it plays one on engineering TV shows
But it's probably an academic issue anyway. And just in case it isn't, we're storing it in double precision. Let's just write the rest of a loop to check it, shall we?
for(i=0; i<100000; i++)
sum += price;
On a Sun machine, this loop yields 100000.000001 in sum
. And that's when using the default rounding impose
d by printf
() on floating point (in reality, it deviates from the real
, correct value before you loop 100
When you're doing computations on money, you literally can't afford to lose money in the cracks in your program. And this is precisely what happens if you use floating point math to keep track of money.
The solutions exist and are well known. Pick one:
- Use BCD floating point, or some other decimal floating point representation. This can accurately represent any decimal fraction, so you don't lose money. Mainframes often support decimal floating point in hardware; "modern" machines don't, but it's possible to build (or buy!) a library and use it.
But you can still lose money due to cancellation when your calculation involves more digits that the accuracy of your representation.
- Use some integer representation to count cents (or ha'pennies, if using). You still have to make sure you write 2 digits to the right of the decimal point (the integer division and modulo operations are your friends here), but you know you're not losing small sums.
You still have to make sure your integers are large enough to hold any sum you may have in your calculations. Or you can use a bigint package. This can be really simple, as you're never going to multiply 2 bigints (since typically only money needs to be a bigint), and in any case your bigints are quite small (32 digits are far more than enough for any sums of money...).