1

I'm trying to simulate the ATM amount input in Java, ie. say the user keeps inputting "1", the amount shown should be:

0.00
0.01
0.11
1.11
11.11
111.11

I tried both Double and BigDecimal for processing:

println( ((new BigDecimal(current)).multiply(new BigDecimal(10)).add(new BigDecimal(0.01))).setScale(2, RoundingMode.HALF_UP).toString()) )

println( "" + Double.parseString(current) * 10 + 0.01 )

However both seems to show this instead:

0.00
0.01
0.11
1.11
11.1 <<< missing the 0.01 at the end
111.01

are they both due to a precision rounding error (I thought BigDecimal does not have this problem) or am I doing something wrong?

James Gu
  • 1,382
  • 4
  • 26
  • 39
  • possible duplicate of [Is floating point math broken?](http://stackoverflow.com/questions/588004/is-floating-point-math-broken) – Anubian Noob Jun 10 '14 at 16:05
  • 4
    IMHO, you should never use BigDecimals or Doubles for currency. If you want the most accuracy convert to pennies. And work with those directly. – Josef E. Jun 10 '14 at 16:08
  • Your code looks okay, but the order of operations on your `println` line might be messing things up. What happens if you move `+ ""` to the end? – Andrew Jun 10 '14 at 16:09
  • 1
    Agree that Doubles should never be used for currencies but BigDecimals can be used - in accountancy systems where you need control over the rounding and sometimes need fractions of a penny/cent they are the best option. – Dave Morrissey Jun 10 '14 at 16:12
  • @DaveMorrissey, Doubles will work just fine... as long as you work in pennies (or whatever the quantum of _your_ currency is called.) If both operands are integer-valued doubles, and the result fits in 51 bits or less, then +, -, and * will all give exact results. – Solomon Slow Jun 10 '14 at 16:26
  • 1
    If you're working in pennies you can use `int`. `BigDecimal` gives you extra flexibility in accountancy and tax calculations. – Dave Morrissey Jun 10 '14 at 16:29

4 Answers4

2

The simplest option might be to record the input as a String. After each key press append the new character to the end of it, and format the number by creating a BigDecimal and dividing it by 100.

String input = "111111";
BigDecimal value = new BigDecimal(input).divide(new BigDecimal(100)); // 1111.11

That said, I've just tried your code in a loop and it appears to work fine. You'll need to post the code showing how you generate current.

String current = "0.00";
for (int i = 0; i < 10; i++) {
    current = (new BigDecimal(current).multiply(new BigDecimal(10)).add(new BigDecimal(0.01))).setScale(2, RoundingMode.HALF_UP).toString();
    System.out.println(current);
}

//    0.01
//    0.11
//    1.11
//    11.11
//    111.11
//    1111.11
//    11111.11
//    111111.11
//    1111111.11
//    11111111.11
Dave Morrissey
  • 4,371
  • 1
  • 23
  • 31
1

Java BigDecimal has a constructor that takes a double and another that takes a string. You are using the double constructor, although the string constructor is usually recommended. Have you tried it?

BigDecimal addend = new BigDecimal("0.01");

For more information, see The Evil Big Decimal Constructor.

rcarraretto
  • 164
  • 2
  • 7
0

It's easier to mess with the string:

public void test() {
    String s = "0.00";
    for (int i = 0; i < 5; i++) {
        s = new BigDecimal(s.replaceAll(Pattern.quote("."), "") + "1").divide(ONEHUNDRED).toString();
        System.out.println(s);
    }
}

Here I remove the ".", add a "1", create a BigDecimal from it and divide that by 100.

OldCurmudgeon
  • 64,482
  • 16
  • 119
  • 213
0

Why do you want to compute an amount on the fly? Just store the string as it comes and parse it at the end.

// Store user input as string (pseudo code)
String readUserInput() {
  String accumulator = "";
  while (user input available) {
    accumulator += user input;
  }
  return accumulator;
}

// Get user input as double
Double getAmount(String input) {
  return Double.parseDouble( input ) / 100.0;
}

// Or a BigDecimal too
BigDecimal getAmount(String input) {
  return new BigDecimal(input).divide(new BigDecimal(100));
}
Vincent Mimoun-Prat
  • 28,208
  • 16
  • 81
  • 124