James Gu
James Gu

Reputation: 1392

Java BigDecimal / Double precision error

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?

Upvotes: 1

Views: 1317

Answers (4)

Dave Morrissey
Dave Morrissey

Reputation: 4421

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

Upvotes: 2

rcarraretto
rcarraretto

Reputation: 164

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.

Upvotes: 1

Vincent Mimoun-Prat
Vincent Mimoun-Prat

Reputation: 28563

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));
}

Upvotes: 0

OldCurmudgeon
OldCurmudgeon

Reputation: 65879

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.

Upvotes: 0

Related Questions