0

I remember this is a problem I can run into, but I forget why. Here's my code.

import java.util.Scanner;

public class GroceryTab
{

    public static void main(String[] args) 
    {
         double total = 0;
         int items = 0;

        System.out.print("How many different products are you buying?");
        Scanner in = new Scanner(System.in);
        items = in.nextInt();

        for(int i=1; i<=items; i++) {
            double price;
            int numberBought;
            System.out.print("What is the price of your " + i +"th item?");
            Scanner priceIn = new Scanner(System.in);
            price = priceIn.nextDouble();

            System.out.print("How many of this item are you buying?");
            Scanner numIn = new Scanner(System.in);
            numberBought = numIn.nextInt();

            total += (price * numberBought);
        }
        System.out.print("Your list costs " + total + " dollars.");
    }
}

Here's the weird part. I was testing it out, and I put in the following:

How many different products are you buying?2

What is the price of your 1th item?30.32

How many of this item are you buying?3

What is the price of your 2th item?.01

How many of this item are you buying?3

and got

Your list costs 90.99000000000001 dollars.

Whoops! What did I do to earn this?

Sharpevil
  • 194
  • 5
  • 18
  • The java floating point will cause that kind of problem – MGPJ Jul 16 '13 at 03:44
  • 5
    Everyone's gotta learn about floating point arithmetic some time. – roippi Jul 16 '13 at 03:45
  • When dealing with money you should probably create a Money class that properly deals with rounding or use BigDecimal , not a float or double. What you are seeing is the inexactness of floating point arithmetic. The way floating point numbers are stored, some don't have an exact representation. If you end up with one of these number s either directly or through a series of operations, you'll need to round it properly. Using a Money class or one that deals directly with values as decimal numbers would solve this problem. – tvanfosson Jul 16 '13 at 03:51

4 Answers4

6

I would just comment, but don't have the rep...

It's because you're using double (or floating point in general). If you need exact precision BigDecimal is better, but slower.

See Floating point arithmetic not producing exact results

Community
  • 1
  • 1
Jafoy
  • 141
  • 2
  • 9
  • BigDecimal is the only correct solution for fixed-precision monetary work in Java. Don't even waste time with hacks like scaled ints -- just do it properly. _(BigDecimal is a scaled int internally, so why you'd waste time duplicating that work -- worse -- and to no advantage -- would be a mystery to me.)_ – Thomas W Jul 16 '13 at 03:51
  • 1
    Point taken. Removed suggestion to count cents over dollars. – Jafoy Jul 16 '13 at 03:55
  • You can say this as an answer. – Sri Harsha Chilakapati Jul 16 '13 at 04:36
0

Floating point (price and total being doubles here) isn't exact; if you want to keep your prices more precise, one possible workaround is to keep track of prices as ints (probably # of cents).

Dennis Meng
  • 5,109
  • 14
  • 33
  • 36
  • BigDecimals are the correct answer, scaled ints are almost definitely not. We're not doing assembly language in 4k of memory here. – Thomas W Jul 16 '13 at 03:48
  • If you're keeping track of prices, it's two extra decimal places to keep track of cents. At that point, whether or not you want to use BigDecimal or internally just keep cents is more of a stylistic issue. Had this been a use case with say, several decimal places, I'd agree with you. – Dennis Meng Jul 16 '13 at 03:52
  • Stylistically, I'd favour encapsulation, future-proofness & correct storage into JDBC/ SQL databases. I do a lot of business-application work and using BigDecimal is just absolutely standard & required. – Thomas W Jul 16 '13 at 04:09
  • Sure, and I'll agree. At the same time, I don't think my answer is worth a downvote because it *does* address the OP's question as to why the original output was slightly off. I will concede that my answer is not worth an upvote. – Dennis Meng Jul 16 '13 at 04:21
  • Thanks for your polite feedback, Dennis. Happy to oblige :) – Thomas W Jul 16 '13 at 08:55
  • (Though seriously, BigDecimals are the proper way to go if you need to deal with anything that has a fixed number of decimal points. Hackarounds like what I suggested are more for options that newer programmers can use if the point of the exercise isn't to learn about dealing with decimals in Java.) – Dennis Meng Jul 17 '13 at 00:55
0

It has to deal with the precision of the double. I would just suggest using

DecimalFormat df = new DecimalFormat("#.00"); 
System.out.print("Your list costs " + df.format(total) + " dollars.");

or something like that, since you are using dollars and will not want .ooooo1 cents. Anyway, thats not your question. Its just the precision of the double isn't the best.

0

Using BigDecimal and doing it correctly:

BigDecimal total = BigDecimal.ZERO;

System.out.print("How many different products are you buying?");
Scanner in = new Scanner(System.in);
int items = in.nextInt();

for (int i=1; i<=items; i++) {
    System.out.print("What is the price of your " + i +"th item?");
    Scanner priceIn = new Scanner(System.in);
    BigDecimal price = priceIn.nextBigDecimal();

    System.out.print("How many of this item are you buying?");
    Scanner numIn = new Scanner(System.in);
    int numberBought = numIn.nextInt();

    BigDecimal lineTot = price.multiply( BigDecimal.valueOf(numberBought));
    total = total.add( lineTot);
}
System.out.print("Your list costs " + total + " dollars.");

Style advice: don't declare variables before you assign a value, if there is only code-path to initially assign that value.

If you show up at a job interview for business software development & don't know how to do fixed-point correctly, you're not going to be hired.

Thomas W
  • 13,940
  • 4
  • 58
  • 76