4

So I'm trying to write a code that will help solve this problem:


Develop a Java program that computes which bills and coins will be given as change for a sales transaction.

Available bills are:

$20

$10

$5

$1

Available coins are:

quarter (25 cents)

dime (10 cents)

nickel (5 cents)

penny (1 cent)

Only print the actual bills and coins returned. Do not print unused denominations (for example, if no $10 bills are returned, do not print anything regarding $10 bills).

Example:

What is the total sales charge? 58.12

How much is the customer giving? 100.00

The change is:

2 $20 bills

1 $1 bill

3 quarters

1 dime

3 pennies


Here's the code that I wrote:

    import java.util.Scanner;

public class Main {

    public static void main(String[] args) {

        Scanner uinput = new Scanner(System.in);
        double paid;
        double cost;
        double change;
        int onebills = 0;
        int fivebills = 0;
        int tenbills = 0;
        int twentybills = 0;

        int quarters = 0;
        int dimes = 0;
        int nickels = 0;
        int pennies = 0;

        System.out.println("What is the total sales charge?");
        cost = uinput.nextDouble();
        System.out.println("");
        System.out.println("How much is the customer giving?");
        paid = uinput.nextDouble();
        change = paid - cost;


        while(change >= 20) { //if the change is more than $20, give a 20 dollar bill
            change -= 20;
            twentybills++;
        }
        while(change >= 10) { //if the change is more than $10, give a 10 dollar bill
            change -= 10;
            tenbills++;
        }
        while(change >= 5) { //if the change is more than $5, give a 5 dollar bill
            change -= 5;
            fivebills++;
        }
        while(change >= 1) { //if the change is more than $1, give a 1 dollar bill
            change -= 1;
            onebills++;
        }

        while(change >= 0.25) { //if the change is more than $0.25, give a quarter
            change -= 0.25;
            quarters++;
        }
        while(change >= 0.10) { //if the change is more than $0.10, give a dime
            change -= 0.10;
            dimes++;
        }
        while(change >= 0.05) { //if the change is more than $0.05, give a nickel
            change -= 0.05;
            nickels++;
        }
        while(change >= 0.01) { //if the change is more than $0.01, give a penny
            change -= 0.01;
            pennies++;
        }

        System.out.println("");
        if(twentybills > 0) {
            System.out.println(twentybills + " twenty dollar bills");
        }
        if(tenbills > 0) {
            System.out.println(tenbills + " ten dollar bills");
        }
        if(fivebills > 0) {
            System.out.println(fivebills + " five dollar bills");
        }
        if(onebills > 0) {
            System.out.println(onebills + " one dollar bills");
        }

        if(quarters > 0) {
            System.out.println(quarters + " quarters");
        }
        if(dimes > 0) {
            System.out.println(dimes + " dimes");
        }
        if(nickels > 0) {
            System.out.println(nickels + " nickels");
        }
        if(pennies > 0) {
            System.out.println(pennies + " pennies");
        }


    } }

For the most part, my code seems to work. However, when I input a sales charge of 99.01 and the customer gives 100, I'm missing one penny. I have absolutely no idea what happened to cause that missing penny.

This is what I end up with: 3 quarters 2 dimes 3 pennies

If anyone can help me find the issue, that would be greatly appreciated! I'm only a high school student learning Java so I don't know much...

  • 2
    Did you try debugging? – shmosel Jun 08 '16 at 22:29
  • What's debugging? Sorry if it's a stupid question but I have absolutely no clue. – Raymond Tan Jun 08 '16 at 22:30
  • 3
    Why the down vote ? i think he is just started and i am pretty sure that down voter wasn't born with the knowledge of how to write good question. Although this question is also not very shabby. –  Jun 08 '16 at 22:31
  • @RaymondTan the process of finding bugs (problems ) in your code is called debugging. You need to learn it. –  Jun 08 '16 at 22:32
  • 1
    Debugging is the process of following your code's execution line by line to see what actually takes place. It's one of the most important skills to acquire when you're beginning to code. See [here](https://www.youtube.com/watch?v=QRIGQs817VA) for a short video tutorial using Eclipse. – shmosel Jun 08 '16 at 22:33
  • 3
    The problem is rounding errors no doubt. – Arjan Jun 08 '16 at 22:34
  • 2
    Here's a page that might be relevant (I haven't ran your code, so it also might not be): http://stackoverflow.com/questions/3730019/why-not-use-double-or-float-to-represent-currency. – Chill Jun 08 '16 at 22:34
  • Correct, I just verified that it's due to rounding errors. That specific input outputs `.98999...` instead of `.99`. – xes_p Jun 08 '16 at 22:37
  • 100 - 99.01 the computer will output 0.9899999999999949 and without rounding will cause error – Seek Addo Jun 08 '16 at 22:38
  • @RaymondTan never trust floating point, they are liars. lol –  Jun 08 '16 at 22:40
  • @RaymondTan you just need to do(change = Math.round(paid - cost);) this and it will help solve the problem but still floating point is a badass – Seek Addo Jun 08 '16 at 22:43

1 Answers1

8

You're suffering from floating point precision error.

This is the output from your program adding a System.out.println(change); after each while loop.

How much is the customer giving?                                                                                                                                                                                                                       
100                                                                                                                                                                                                                                                    
0.9899999999999949                                                                                                                                                                                                                                     
0.9899999999999949                                                                                                                                                                                                                                     
0.9899999999999949                                                                                                                                                                                                                                     
0.9899999999999949                                                                                                                                                                                                                                     
0.23999999999999488                                                                                                                                                                                                                                    
0.03999999999999487                                                                                                                                                                                                                                    
0.009999999999994869                                                                                                                                                                                                                                   

3 quarters                                                                                                                                                                                                                                             
2 dimes                                                                                                                                                                                                                                                
3 pennies           

See how when you get into the change, you don't have nice numbers like .99. You have 0.9899999999999949. This is because the IEEE-754 standard for floating point numbers has some limitations, which cause slight amounts of error for any decimal that can't be represented in terms of powers of 2. Usually, the error is too small to cause problems, but you happened to find a test case where you suffered from it.

So what can you do? The easiest solution in my opinion would be to convert your input to all integers and keep track of numbers in terms of pennies, instead of of dollars. That would avoid the floating point precision issue.

Other languages, such as C#, actually provide a different data type from float or double specifically for dealing with numbers that require precision in base-10 numbers. C#'s, for example, is decimal. I am not aware of an equivalent in Java, so this is something you have to keep in mind when working with any floating point number

As another example of the horrors of floating point error, what would you expect this to print?

if(.1 + .2 == .3)
  System.out.println("TRUE");
else
  System.out.println("FALSE");

Make your guess, then check your answer...

Tyler Lee
  • 2,736
  • 13
  • 24
  • To add onto this answer, a simple fix would to just multiply the input by 100, and all subsequent checks. I.e. `cost = uinput.nextInt() * 100` instead of `cost = uinput.nextDouble()` and `while (change >= 500)` instead of `while (change >= 5)`. Note how we're now using integers, so adjust your variables accordingly. – xes_p Jun 08 '16 at 22:40
  • 1
    @EdwardShen That's exactly what was suggested. Another option is to use `BigDecimal`. – shmosel Jun 08 '16 at 22:41
  • Beat me to it :) I'll delete my answer and upvote yours! – Arjan Jun 08 '16 at 22:42
  • @shmosel The comment I posted was meant for the original poster, not the answerer. My apologies for any confusion. – xes_p Jun 08 '16 at 22:42
  • @EdwardShen, you still would want to use `nextDouble()` as the user will likely enter in the familiar dollars.cents notation. – Tyler Lee Jun 08 '16 at 22:57
  • @Tyler Yes, you're correct. So the original poster would want to cast it to an integer ( `cost = (int) (uinput.nextDouble() * 100)`) – xes_p Jun 08 '16 at 22:59