17

Possible Duplicate:
Why not use Double or Float to represent currency?

I'm writing a basic command-line program in Java for my high school course. We're only working with variables right now. It's used to calculate the amount of bills and coins of whatever type in your change after a purchase. This is my program:

class Assign2c {
    public static void main(String[] args) {
        double cost = 10.990;
        int paid = 20;
        double change = paid - cost;
        int five, toonie, loonies, quarter, dime, nickel, penny;

        five = (int)(change / 5.0);
        change -= five * 5.0;

        toonie = (int)(change / 2.0);
        change -= toonie * 2.0;

        loonies = (int)change;
        change -= loonies;

        quarter = (int)(change / 0.25);
        change -= quarter * 0.25;

        dime = (int)(change / 0.1);
        change -= dime * 0.1;

        nickel = (int)(change / 0.05);
        change -= nickel * 0.05;

        penny = (int)(change * 100);
        change -= penny * 0.01;

        System.out.println("$5   :" + five);
        System.out.println("$2   :" + toonie);
        System.out.println("$1   :" + loonies);
        System.out.println("$0.25:" + quarter);
        System.out.println("$0.10:" + dime);
        System.out.println("$0.05:" + nickel);
        System.out.println("$0.01:" + penny);
    }
}

It should all work but at the last step when there's $0.01 leftover, number of pennies should be 1 but instead, it's 0. After a few minutes of stepping into the code and outputting the change value to the console, I've found out that at the last step when change = 0.01, it changes to 0.009999999999999787. Why is this happening?

Community
  • 1
  • 1
Alex
  • 3,111
  • 6
  • 27
  • 43
  • You can't represent all decimal fractions correctly in binary. Use integers to do these operations, or handle the rounding yourself. This question (or a variant of it) has been asked here hundreds of times. Here's a good reference to check out: http://download.oracle.com/docs/cd/E19957-01/806-3568/ncg_goldberg.html – Carl Norum Sep 13 '11 at 21:15
  • it's easier just to do mod :P – Kevin Sep 13 '11 at 21:17
  • 10
    Aaaand there we go again..... – Martijn Courteaux Sep 13 '11 at 21:20
  • even I never used BigDecimal (because there I found difference to MsExcell, but that only my issue) please read [one thread from OTN][1], [1]: https://forums.oracle.com/forums/thread.jspa?threadID=2281852&tstart=0 – mKorbel Sep 13 '11 at 21:33

8 Answers8

10

Using double for currency is a bad idea, Why not use Double or Float to represent currency?. I recommend using BigDecimal or doing every calculation in cents.

Community
  • 1
  • 1
Amir Raminfar
  • 33,777
  • 7
  • 93
  • 123
  • 1
    Doing all calculations in cents is almost always preferable to using `BigDecimal`, IMO. – Matt Ball Sep 13 '11 at 21:17
  • @Matt Ball That has the obvious problems of fixed point arithmetic, ie you either lose accuracy for not round numbers or need another solution anyhow. For the intermediary computations having only an accuracy of up to 1 cent sounds like a recipe for disaster. – Voo Sep 13 '11 at 21:20
  • @Voo: For the application the user is doing, accuracy of one cent is perfectly fine. – Oliver Charlesworth Sep 13 '11 at 21:25
  • @Oli Charlesworth You mean an application with no real use? ;) Sure it may be fine, but it's always a good idea to know all advantages and disadvantages of a possible solution (especially if the only reason you're doing it is to learn something new). And calculating in cents is certainly not "almost always preferable" to BigDecimals imo - not if accuracy is important (and depending on what calculations you do the end result may vary by far more than 1 cent obviously). The only downside to bigdecimals I can see is performance – Voo Sep 13 '11 at 21:31
7

0.01 does not have an exact representation in floating-point (and neither do 0.1 nor 0.2, for that matter).

You should probably do all your maths with integer types, representing the number of pennies.

Oliver Charlesworth
  • 267,707
  • 33
  • 569
  • 680
4

doubles aren't kept in decimal internally, but in binary. Their storage format is equivalent to something like "100101 multiplied by 10000" (I'm simplifying, but that's the basic idea). Unfortunately, there's no combination of these binary values that works out to exactly decimal 0.01, which is what the other answers mean when they say that floating point numbers aren't 100% accurate, or that 0.01 doesn't have an exact representation in floating point.

There are various ways of dealing with this problem, some more complicated than others. The best solution in your case is probably to use ints everywhere and keep the values in cents.

Jon Bright
  • 13,388
  • 3
  • 31
  • 46
1

Floating point numbers are never 100% accurate (not quite true, see comments below). You should never compare them directly. Also integer rounding. The best way to do this would probably be to do it in cents and convert to dollars later (1 dollar == 100 cents). By converting to an integer you are losing precision.

joshx0rfz
  • 19
  • 3
  • 2
    It's not true to say that "floating point numbers are never 100% accurate". – Oliver Charlesworth Sep 13 '11 at 21:17
  • I guess I should say you can't reliably compare them? – joshx0rfz Sep 13 '11 at 21:20
  • 0.5 is 100% accurate. As are (or can be) most powers of 2, and most multiples thereof (as long as the mantissa can hold the numerator of the fraction). It's when you get to fractions where the denominator isn't a power of 2, or the numerator is too large to represent fully, that you run into rounding errors. – cHao Sep 13 '11 at 21:21
1

As the others already said, do not use doubles for financial calculations.

This paper http://download.oracle.com/docs/cd/E19957-01/806-3568/ncg_goldberg.html (What Every Computer Scientist Should Know About Floating-Point Arithmetic) is a must-read to understand floating point math in computers.

fvu
  • 32,488
  • 6
  • 61
  • 79
0

its a float(double)

You should not use it to compute money....

I recommend using int values and operate on pennys

l_39217_l
  • 2,090
  • 1
  • 13
  • 15
0

This is a problem that's arisen many times over. The bottom line is that on a computer that uses binary floating point (which Java requires), only fractions in which the denominator is a power of 2 can be represented precisely.

The same problem arises in decimal. 1/3, for example, turns into 0.3333333..., because 3 isn't a factor of 10 (the base we're using in decimal). Likewise 1/17, 1/19, etc.

In binary floating point, the same basic problem arises. The main difference is that in decimal, since 5 is a factor of 10, 1/5 can be represented precisely (and so can multiples of 1/5). Since 5 is not a factor of 2, 1/5 cannot be represented precisely in binary floating point.

Contrary to popular belief, however, some fractions can be represented precisely -- specifically those fractions whose denominators with only 2 as a prime factor (e.g., 1/8 or 1/256 can be represented precisely).

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
0

I'm sure you know that some fractions' decimal representations terminate (e.g. .01) while some don't (e.g. 2/3=.66666...). The thing is that which fractions terminate changes depending on what base you're in; in particular, .01 doesn't terminate in binary, so even though double provides a lot of precision it can't represent .01 exactly. As others said, using BigDecimal or fixed-point integer computations (converting everything to cents) is probably best for currency; to learn more about floating point, you could start at The Floating-Point Guide- What Every Programmer Should Know About Floating-Point Arithmetic.

Prodicus
  • 447
  • 2
  • 7