Nital
Nital

Reputation: 6114

How to convert dollar amount to cents in java?

I have the following program (taken from SO link What is the best way to convert Dollars (Big Decimal) in Cents (Integer) in java?) which converts dollar to cents. However, the output is not as per my expectations.

Current Output:

12345
8

Expected Output:

12345
9

Main.java

public class Main {

    private static final BigDecimal x100 = new BigDecimal(100);
    static List<BigDecimal> nums = new ArrayList<>();
    static BigDecimal a = new BigDecimal(123.45);
    static BigDecimal b = new BigDecimal(0.09);

    public static void main(String[] args) {
        nums.add(a);
        nums.add(b);
        for (BigDecimal usd : nums) {
            BigDecimal rounded = usd.setScale(2, BigDecimal.ROUND_DOWN);
            BigDecimal bigDecimalInCents = rounded.multiply(x100);
            int cents = bigDecimalInCents.intValueExact();
            System.out.println(cents);
        }
    }

}

Upvotes: 0

Views: 5968

Answers (2)

user695022
user695022

Reputation: 589

Your issue is that doubles cannot represent all values. 9/100, for example, can only be approximated. Try System.out.println(new BigDecimal(0.09)). You should get 0.0899999999999999966693309261245303787291049957275390625. So you're rounding that down to 0.08 and then multiplying by 100.

Try new BigDecimal(String.valueOf(0.09))

Edit: A better solution is to use BigDecimal.valueOf(0.09), which is equivalent.

From the javadoc for BigDecimal(double):

Translates a double into a BigDecimal which is the exact decimal representation of the double's binary floating-point value.
...
When a double must be used as a source for a BigDecimal, note that this constructor provides an exact conversion; it does not give the same result as converting the double to a String using the Double.toString(double) method and then using the BigDecimal(String) constructor. To get that result, use the static valueOf(double) method.

And for BigDecimal.valueOf(double):

Translates a double into a BigDecimal, using the double's canonical string representation provided by the Double.toString(double) method.

Note: This is generally the preferred way to convert a double (or float) into a BigDecimal, as the value returned is equal to that resulting from constructing a BigDecimal from the result of using Double.toString(double).

Upvotes: 4

Basil Bourque
Basil Bourque

Reputation: 339659

tl;dr

Use strings, not numeric literals.

new BigDecimal( "123.45" )

Avoid floating-point

You implicitly introduced double type primitives into your code when constructing your BigDecimal.

new BigDecimal( 123.45 )  // <-- Passing `double` primitive
new BigDecimal( 0.09 )

While you thought you were passing 123.45 you were actually passing something more like 123.4500000000000028421709430404007434844970703125. And instead of 0.09 you were passing something more like 0.0899999999999999966693309261245303787291049957275390625.

You defeated the very purpose of using BigDecimal with currency which is to avoid the inherent inaccuracy of floating-point types in determining their decimal fraction.

The Java types float, Float, double, Double all use floating point technology. Floating point technology trades away accuracy for speed of execution in calculations. Good for some gaming, graphics, and scientific/engineering purposes but bad for money.

BigDecimal does not use floating point technology. So the execution of calculations is much slower, but the results are accurate.

Solution: Avoid floating-point when constructing BigDecimal. Use strings.

new BigDecimal( "123.45" )
new BigDecimal( "0.09" )

Upvotes: 5

Related Questions