Bigdecimal Adding Wrong Value

BigDecimal adding wrong value

Use a String literal:

private static final BigDecimal sd = new BigDecimal("0.7");

If you use a double, actually public BigDecimal(double val) is called. The reason you do not get 0.7 is that it cannot be exactly represented by a double. See the linked JavaDoc for more information.

BigDecimal divide returning wrong result for some calculations

The problem is that you're using the wrong BigDecimal#divide for your rounding to 2 decimals. Here are the available arguments for the BigDecimal#divide methods:

  • divide(BigDecimal divisor)
  • divide(BigDecimal divisor, int roundingMode)
  • divide(BigDecimal divisor, MathContext mc)
  • divide(BigDecimal divisor, RoundingMode roundingMode)
  • divide(BigDecimal divisor, int scale, int roundingMode)
  • divide(BigDecimal divisor, int scale, RoundingMode roundingMode)

Since you're using a divide with a BigDecimal and int argument, it therefore uses the divide(BigDecimal divisor, int roundingMode), where your 2 is the rounding mode and NOT the scale. In this case, 2 is actually ROUND_CEILING, and scale is unspecified.

Instead, you'll have to use either the divide(BigDecimal divisor, int scale, int roundingMode) or divide(BigDecimal divisor, int scale, RoundingMode roundingMode). So change your calls to:

System.out.println(BigDecimal.valueOf(2.8).divide(BigDecimal.valueOf(3.87), 2, 
BigDecimal.ROUND_HALF_UP));
System.out.println(BigDecimal.valueOf(2.41).divide(BigDecimal.valueOf(2.73), 2,
BigDecimal.ROUND_HALF_UP));

(Feel free to use a rounding mode other than ROUND_HALF_UP.)

Try it online.

I'm not sure what scale it uses by default, but the ROUND_CEILING you've specified with your 2 caused the issues in your calculations.


As for the mentioned comments, there are three possible ways to create the BigDecimal with the specified values:

  • BigDecimal.valueOf(2.8)
  • new BigDecimal(2.8)
  • new BigDecimal("2.8")

The new BigDecimal(2.8) still gives floating point errors, so I would advice to use either BigDecimal.valueOf(2.8) as you already did, or the String constructor new BigDecimal("2.8").

Try it online.

This is all a bit irrelevant for your rounding issues however, since using the correct divide method will give the correct results regardless of the BigDecimal initialization you've used:

Try it online.

BigDecimal from Double incorrect value?

When you create a double, the value 0.3 cannot be represented exactly. You can create a BigDecimal from a string without the intermediate double, as in

new BigDecimal("0.3")

A floating point number is represented as a binary fraction and an exponent. Therefore there are some number that cannot be represented exactly. There is an analogous problem in base 10 with numbers like 1/3, which is 0.333333333..... Any decimal representation of 1/3 is inexact. This happens to a DIFFERENT set of fractions in binary, and 0.3 is one of the set that is inexact in binary.

BigDecimal.Divide gives incorrect result

You asked the divide() call for 13 digits of precision, and that is what it gave you.

You then convert that to double and notice that double cannot handle that precision.

BigDecimal d1 = new BigDecimal(String.valueOf(221500.0)).setScale(13, BigDecimal.ROUND_HALF_UP);
BigDecimal d2 = new BigDecimal(String.valueOf(12.0)).setScale(13, BigDecimal.ROUND_HALF_UP);
BigDecimal d3 = d1.divide(d2, 13, BigDecimal.ROUND_HALF_UP);
System.out.println("BigDecimal: " + d3);
System.out.println("as double : " + d3.doubleValue());

OUTPUT

BigDecimal: 18458.3333333333333
as double : 18458.333333333332

If you're going to throw away the extra precision gained by using BigDecimal, why not just do the math in double?


On a different note:

Don't do new BigDecimal(String.valueOf(doubleValue)).

Use BigDecimal.valueOf(doubleValue).

Setting the scale of d1 and d2 seems rather meaningless.

Unless explicitly needed, round as late as possible, if at all.

Multiplying two numbers using BigDecimal returns a wrong value

You're not multiplying two numbers using BigDecimal. You're multiplying them using double arithmetic, and passing the result to the BigDecimal constructor.

You want:

new BigDecimal("0.06").multiply(new BigDecimal("3")).toString()

Note that you do want the values in strings - otherwise you're using the double value for 0.06, which isn't exactly 0.06... you've lost information before you start. (You don't really need the string form of 3, but I've done so for consistency.)

For example:

System.out.println(new BigDecimal(0.06));

prints

0.059999999999999997779553950749686919152736663818359375

BigDecimal Error

You're still going via double. Stick to BigDecimal everywhere:

BigDecimal k = BigDecimal.ZERO;
while (rst.next()) {
k = k.add(new BigDecimal(rst.getString(5));
}

Alternatively - and preferrably, if the field in the database is actually a decimal value:

BigDecimal k = BigDecimal.ZERO;
while (rst.next()) {
k = k.add(rst.getBigDecimal(5));
}

Java BigDecimal operations arbitrarily showing incorrect number of decimal places

The problem is probably a misunderstanding of MathContext. With MathContext(2, RoundingMode.UP) you're not saying "I want 2 decimal places", but you say "I want to reduce the precision of my number to 2 digits".

If you have a look at your numbers, the scale (number of decimal places) is 2 where hours - lim >= 10

If you have a detailed look at the two examples hours=12.5 and hours=13.0

//hours = 12.5:
var subtract = hours.subtract("3.00"); // 12.5 - 3.00 = 9.50 (subtract.scale = 2)
var round = subtract.round(mc); // 9.5 (round.scale = 1)
var result = round.multiply("0.50") // 9.5 * 0.50 = 4.750 (result.scale = round.scale + subtract.scale = 3)
//hours = 13.0:
var subtract = hours.subtract("3.00"); // 13.0 - 3.00 = 10.00 (subtract.scale = 2)
var round = subtract.round(mc); // 10 (round.scale = 0)
var result = round.multiply("0.50") // 10 * 0.50 = 5.00 (result.scale = round.scale + subtract.scale = 2)

You see the scale of the rounded decimal becomes 0 as soon as it's value becomes >= 10. The scale of the result of multiply is always the sum of the scale of the two multiplicants.

To set the scale of a BigDecimal use BigDecimal.setScale(int, RoundingMode) instead of MathContext.



Related Topics



Leave a reply



Submit