0

So for another project I am supposed to create a program that prompts the user for a monetary value and prints out the least number of bill and coin starting with the highest. So for example, if the user input 47.63, the output would be:

0 hundreds 2 twenties 0 tens, etc.

My problem is that when i put in a certain value (namely 186.41), I should come out with 1 Hundreds 1 Fifties 1 Twenties 1 Tens 1 Fives 1 Ones 1 Quarters 1 Dimes 1 Nickles 1 Pennies.

However, my output in the pennies says "0 pennies" Here's my code:

 public class CountingMoney {
    public static BufferedReader delta = new BufferedReader(new InputStreamReader(System.in));
    public static void main(String [] args) throws IOException{
        run();
    }

    public static void run() throws IOException{
        System.out.println("Please enter your monetary value");
        String userinput = delta.readLine();
        double input = Double.parseDouble(userinput);

        int amount = (int) (input / 100);
        input -= amount * 100;
        System.out.println(amount+ " Hundreds");

        amount = (int) (input/50);
        input -= amount * 50;
        System.out.println(amount + " Fifties");

        amount = (int) (input/20);
        input -= amount * 20;
        System.out.println(amount + " Twenties");

        amount = (int) (input/10);
        input -= amount*10;
        System.out.println(amount + " Tens");

        amount = (int) (input/5);
        input -= amount *5;
        System.out.println(amount + " Fives");

        amount = (int) (input/1);
        input -= amount *1;
        System.out.println(amount + " Ones");

        amount = (int) (input/.25);
        input -= amount * .25;
        System.out.println(amount + " Quarters");

        amount = (int) (input/.10);
        input -= amount * .10;
        System.out.println(amount + " Dimes");

        amount = (int) (input/.05);
        input -= amount * .05;
        System.out.println(amount + " Nickles");

        amount = (int) (input/.01);
        input -= amount * .01;
        System.out.println(amount + " Pennies");

    }

}
Gautam
  • 3,707
  • 5
  • 36
  • 57
MehLdy
  • 57
  • 1
  • 7
  • Looks like it's probably a floating point rounding error. You should never use `double` for an amount of money, if you care about accuracy. Otherwise, very small errors keep creeping in, like the one you're seeing. Try using `BigDecimal` instead of `double`. – Dawood ibn Kareem Jan 20 '17 at 05:28
  • @DavidWallace thank you so much – MehLdy Jan 20 '17 at 05:32
  • @DavidWallace is right about floating-point errors--a value like 186.41 cannot be represented exactly as a double. However, the real problem here is that when you cast a `double` to an `int`, _it always rounds down_. If you print `input` just before dividing it by 0.01, it displays 0.009999999999996581. When you divide by 0.01, you'll get something like 0.9999999999996581. This is very close to 1, so the amount of floating-point error is very tiny. But it's still less than 1, which means `(int)` results in 0. Using `Math.round()` instead of casting to `int` would solve this... – ajb Jan 20 '17 at 06:01
  • 1
    ... but I'd still recommend either using `BigDecimal`, or doing everything in integers by letting the amounts represent the number of cents instead of the number of dollars. – ajb Jan 20 '17 at 06:05

4 Answers4

1

Below java program demonstrate how to convert a number from zero to one million.

NumberToStringLiteral class :

public class NumberToStringLiteral
{

    public static void main(String[] args)
    {
        NumberToStringLiteral numberToStringLiteral = new NumberToStringLiteral();
        int number = 123456;
        String stringLiteral = numberToStringLiteral.convertIntegerToStringLiteral(number);
        System.out.println(stringLiteral);
    }
    
    private String convertIntegerToStringLiteral(int number)
    {
        if (number < 100)
            return from_0_To_100(number);
        
        if ( number >= 100 && number < 1000 )
            return from_101_To_999(number);
        
        if ( number >= 1000 && number <= 99999)
            return from_1000_and_99999(number);
        
        if (number <= 1000000)
            return from_100000_and_above(number);
        
        return Digits.OVER_ONE_MILLION.getStringLiteral();
    }
    
    private String from_0_To_100(int number)
    {
        if (number <= 19 )
            return ZeroToNineteen.getStringLiteral(number);
        
        String LastDigit = ( ZeroToNineteen.getStringLiteral(number % 10) != ZeroToNineteen.ZERO.getStringLiteral() ) ? 
                            ZeroToNineteen.getStringLiteral(number % 10) : "";
        return Tens.getStringLiteralFromNumber( (number - (number % 10 )) ) + " " + LastDigit;
    }
    
    private String from_101_To_999(int number)
    {
        String LastDigit = ( ZeroToNineteen.getStringLiteral(number % 100) != ZeroToNineteen.ZERO.getStringLiteral() ) ?
                            ZeroToNineteen.getStringLiteral(number % 100)  : "";
        
        if ( (number % 100) > 19)
            LastDigit = from_0_To_100(number % 100);
            
        if (LastDigit.isBlank())
            return ZeroToNineteen.getStringLiteral(number / 100 ) + Digits.getStringLiteral(getNumberOfDigit(0));
            
        return ZeroToNineteen.getStringLiteral(number / 100 ) + Digits.getStringLiteral(getNumberOfDigit(number)) + LastDigit;
    }
        
    private String from_1000_and_99999(int number)
    {
        String LastDigit = (number % 1000 < 20 ) ? from_0_To_100(number % 1000) : from_101_To_999(number % 1000);
        
        if (LastDigit.equalsIgnoreCase(ZeroToNineteen.ZERO.getStringLiteral()))
            LastDigit = "";
        
        return from_0_To_100(number / 1000 ) + Digits.getStringLiteral(getNumberOfDigit(number)) + LastDigit;
    }
    
    private String from_100000_and_above(int number)
    {
        if (number == 1000000)
            return Digits.ONE_MILLION.getStringLiteral();
        
        String lastThreeDigit = (number % 1000 <= 100)  ? from_0_To_100(number % 1000) : from_101_To_999(number % 1000);
        
        if (lastThreeDigit.equalsIgnoreCase(ZeroToNineteen.ZERO.toString()))
            lastThreeDigit = "";
        
        String number1 = from_101_To_999(number / 1000) + Digits.THOUSAND.getStringLiteral() +  lastThreeDigit;
        return String.valueOf(number1);
    }
    
    private int getNumberOfDigit(int number)
    {
        int count = 0;
        while ( number != 0 )
        {
            number /=  10;
            count++;
        }
        return count;
    }
}

ZeroToNineteen enum :

public enum ZeroToNineteen
{
    ZERO(0, "zero"), 
    ONE(1, "one"),
    TWO(2, "two"),
    THREE(3, "three"),
    FOUR(4, "four"),
    FIVE(5, "five"),
    SIX(6, "six"),
    SEVEN(7, "seven"),
    EIGHT(8, "eight"),
    NINE(9, "nine"),
    TEN(10, "ten"),
    ELEVEN(11, "eleven"),
    TWELVE(12, "twelve"),
    THIRTEEN(13, "thirteen"),
    FOURTEEN(14, "fourteen"),
    FIFTEEN(15, "fifteen"),
    SIXTEEN(16, "sixteen"),
    SEVENTEEN(17, "seventeen"),
    EIGHTEEN(18, "eighteen"),
    NINETEEN(19, "nineteen");
    
    
    private int number;
    private String stringLiteral;
    public static Map<Integer, String> stringLiteralMap;
    
    ZeroToNineteen(int number, String stringLiteral)
    {
        this.number = number;
        this.stringLiteral = stringLiteral;
    }
    
    public int getNumber()
    {
        return this.number;
    }
    
    public String getStringLiteral()
    {
        return this.stringLiteral;
    }
    
    public static String getStringLiteral(int number)
    {
        if (stringLiteralMap == null)
            addData();
        
        return stringLiteralMap.get(number);
    }
    
    private static void addData()
    {
        stringLiteralMap = new HashMap<>();
        for (ZeroToNineteen zeroToNineteen : ZeroToNineteen.values())
        {
            stringLiteralMap.put(zeroToNineteen.getNumber(), zeroToNineteen.getStringLiteral());
        }
    }
}

Tens enum :

public enum Tens
{
    TEN(10, "ten"),
    TWENTY(20, "twenty"),
    THIRTY(30, "thirty"),
    FORTY(40, "forty"),
    FIFTY(50, "fifty"),
    SIXTY(60, "sixty"),
    SEVENTY(70, "seventy"),
    EIGHTY(80, "eighty"),
    NINETY(90, "ninety"),
    HUNDRED(100, "one hundred");
    
    
    private int number;
    private String stringLiteral;
    private static Map<Integer, String> stringLiteralMap;
    
    Tens(int number, String stringLiteral)
    {
        this.number = number;
        this.stringLiteral = stringLiteral;
    }
    
    public int getNumber()
    {
        return this.number;
    }
    
    public String getStringLiteral()
    {
        return this.stringLiteral;
    }
    
    public static String getStringLiteralFromNumber(int number)
    {
        if (stringLiteralMap == null)
            addDataToStringLiteralMap();
        
        return stringLiteralMap.get(number);
    }
    
    private static void addDataToStringLiteralMap()
    {
        stringLiteralMap = new HashMap<Integer, String>();
        for (Tens tens : Tens.values())
            stringLiteralMap.put(tens.getNumber(), tens.getStringLiteral());
    }
}

Digits enum :

public enum Digits
{
    HUNDRED(3, " hundred and "),
    THOUSAND(4, " thousand "),
    TEN_THOUSAND(5," thousand "),
    ONLY_HUNDRED(0, " hundred" ),
    ONE_MILLION(1000000, "one million"),
    OVER_ONE_MILLION(1000001, "over one million");
    
    private int digit;
    private String stringLiteral;
    private static Map<Integer, String> stringLiteralMap;
    
    private Digits(int digit, String stringLiteral)
    {
        this.digit = digit;
        this.stringLiteral = stringLiteral;
    }
    
    public int getDigit()
    {
        return this.digit;
    }
    
    public String getStringLiteral()
    {
        return this.stringLiteral;
    }
    
    public static String getStringLiteral(int number)
    {
        if ( stringLiteralMap == null )
            addStringLiteralMap();
        
        return stringLiteralMap.get(number);
    }
    
    private static void addStringLiteralMap()
    {
        stringLiteralMap = new HashMap<Integer, String>();
        for ( Digits digits : Digits.values() )
            stringLiteralMap.put(digits.getDigit(), digits.getStringLiteral());
    }
}

Output :

one hundred and twenty three thousand four hundred and fifty six

Note: I have used all three enum for constant variables, you can also use array.

Hope this will help you, Let me know if you have any doubt in comment section.

0

The issue you're facing is better explained over here.

To overcome this use BigDecimal. However make sure you set the Scale and RoundingMode.
If not you might end up with java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result. during your divide operation.

Sample Code on how to modify:

amount = bigDecimal.divide(new BigDecimal(100), 2, RoundingMode.HALF_UP).intValue();
bigDecimal = bigDecimal.subtract(new BigDecimal(amount * 100));
System.out.println(amount+ " Hundreds");
Community
  • 1
  • 1
Kishore Bandi
  • 5,537
  • 2
  • 31
  • 52
0

When you cast a double to an int with (int), it always rounds down, no matter how close the double value is to the integer just above it. doubles cannot represent values like 186.41 exactly, because they're stored in binary instead of decimal. When you start doing lots of calculations with them, the errors start accumulating.

When I try your program, and show the value that's being rounded:

    System.out.println("result of division is " + (input/.01));
    amount = (int) (input/.01);
    input -= amount * .01;
    System.out.println(amount + " Pennies");

it displays

    result of division is 0.999999999999658

This is very, very close to 1, so not that much error has been accumulated. But it's not exact. And since casting to (int) rounds down, amount will be 0.

There are two ways to solve this:

1) Use BigDecimal as suggested in the other answer. This is the preferred method of dealing with money. It will represent decimal places exactly.

2) Use Math.round() instead of (int), e.g.

amount = (int) Math.round(input / .01);

Math.round on a double returns a long, so you still need a cast, but it's casting from an integer to another integer so no rounding is involved.

I still recommend using BigDecimal. However, this should help you see that in other situations, where you do need to deal with double, you need to pay attention to what kind of rounding you do. Casting to (int) will often be wrong, depending on what you're trying to do.

ajb
  • 31,309
  • 3
  • 58
  • 84
0

You can use below code and it is available in this link

For more reference and discussion you can refer this stack-overflow post

Hope it will solve your need.

package com.test;

import java.text.DecimalFormat;

public class EnglishNumberToWords {

private static final String[] tensNames = {
    "",
    " ten",
    " twenty",
    " thirty",
    " forty",
    " fifty",
    " sixty",
    " seventy",
    " eighty",
    " ninety"
};

private static final String[] numNames = {
    "",
    " one",
    " two",
    " three",
    " four",
    " five",
    " six",
    " seven",
    " eight",
    " nine",
    " ten",
    " eleven",
    " twelve",
    " thirteen",
    " fourteen",
    " fifteen",
    " sixteen",
    " seventeen",
    " eighteen",
    " nineteen"
};

private EnglishNumberToWords() {}

private static String convertLessThanOneThousand(int number) {
    String soFar;

    if (number % 100 < 20){
        soFar = numNames[number % 100];
        number /= 100;
    }
    else {
        soFar = numNames[number % 10];
        number /= 10;

        soFar = tensNames[number % 10] + soFar;
        number /= 10;
    }
    if (number == 0) {
        return soFar;
    }
    return numNames[number] + " hundred" + soFar;
}


public static String convert(long number) {
    // 0 to 999 999 999 999
    if (number == 0) { return "zero"; }

    String snumber = Long.toString(number);

    // pad with "0"
    String mask = "000000000000";
    DecimalFormat df = new DecimalFormat(mask);
    snumber = df.format(number);

    // XXXnnnnnnnnn
    int billions = Integer.parseInt(snumber.substring(0,3));
    // nnnXXXnnnnnn
    int millions  = Integer.parseInt(snumber.substring(3,6));
    // nnnnnnXXXnnn
    int hundredThousands = Integer.parseInt(snumber.substring(6,9));
    // nnnnnnnnnXXX
    int thousands = Integer.parseInt(snumber.substring(9,12));

    String tradBillions;
    switch (billions) {
        case 0:
            tradBillions = "";
            break;
        case 1 :
            tradBillions = convertLessThanOneThousand(billions)
            + " billion ";
            break;
        default :
            tradBillions = convertLessThanOneThousand(billions)
            + " billion ";
    }
    String result =  tradBillions;

    String tradMillions;
    switch (millions) {
        case 0:
            tradMillions = "";
            break;
        case 1 :
            tradMillions = convertLessThanOneThousand(millions)
            + " million ";
            break;
        default :
            tradMillions = convertLessThanOneThousand(millions)
            + " million ";
    }
    result =  result + tradMillions;

    String tradHundredThousands;
    switch (hundredThousands) {
        case 0:
            tradHundredThousands = "";
            break;
        case 1 :
            tradHundredThousands = "one thousand ";
            break;
        default :
            tradHundredThousands = convertLessThanOneThousand(hundredThousands)
            + " thousand ";
    }
    result =  result + tradHundredThousands;

    String tradThousand;
    tradThousand = convertLessThanOneThousand(thousands);
    result =  result + tradThousand;

    // remove extra spaces!
    return result.replaceAll("^\\s+", "").replaceAll("\\b\\s{2,}\\b", " ");
}

/**
 * testing
 * @param args
 */
public static void main(String[] args) {
    System.out.println("*0* " + EnglishNumberToWords.convert(0));
    System.out.println("*1* " + EnglishNumberToWords.convert(1));
    System.out.println("*16* " + EnglishNumberToWords.convert(16));
    System.out.println("*100* " + EnglishNumberToWords.convert(100));
    System.out.println("*118* " + EnglishNumberToWords.convert(118));
    System.out.println("*200* " + EnglishNumberToWords.convert(200));
    System.out.println("*219* " + EnglishNumberToWords.convert(219));
    System.out.println("*800* " + EnglishNumberToWords.convert(800));
    System.out.println("*801* " + EnglishNumberToWords.convert(801));
    System.out.println("*1316* " + EnglishNumberToWords.convert(1316));
    System.out.println("*1316* " + EnglishNumberToWords.convert(1316));
    System.out.println("*2000000* " + EnglishNumberToWords.convert(2000000));
    System.out.println("*3000200* " + EnglishNumberToWords.convert(3000200));
    System.out.println("*700000* " + EnglishNumberToWords.convert(700000));
    System.out.println("*9000000* " + EnglishNumberToWords.convert(9000000));
    System.out.println("*9001000* " + EnglishNumberToWords.convert(9001000));
    System.out.println("*123456789* " + EnglishNumberToWords.convert(123456789));
    System.out.println("*2147483647* " + EnglishNumberToWords.convert(2147483647));
    System.out.println("*3000000010L* " + EnglishNumberToWords.convert(3000000010L));


}

}

Community
  • 1
  • 1
Gautam
  • 3,707
  • 5
  • 36
  • 57