Shivam Patil
Shivam Patil

Reputation: 1

BigDecimal with RoundingMode.FLOOR is giving unexpected result

I am getting an unexpected result I want to remove numbers after two number of decimal point

BigDecimal(0.55).setScale(2, RoundingMode.FLOOR) //return 0.55
BigDecimal(0.56).setScale(2, RoundingMode.FLOOR) //return 0.56
BigDecimal(0.57).setScale(2, RoundingMode.FLOOR) //return 0.56
BigDecimal(**0.58**).setScale(2, RoundingMode.FLOOR) //return 0.57
BigDecimal(**0.59**).setScale(2, RoundingMode.FLOOR) //return 0.58
BigDecimal(**0.60**).setScale(2, RoundingMode.FLOOR) //return 0.59
BigDecimal(**0.61**).setScale(2, RoundingMode.FLOOR) //return 0.60
BigDecimal(**0.62**).setScale(2, RoundingMode.FLOOR) //return 0.61
BigDecimal(0.63).setScale(2, RoundingMode.FLOOR) //return 0.63

Upvotes: 0

Views: 1796

Answers (2)

gidds
gidds

Reputation: 18537

The problem here is not in the setScale() call, but happens before that.  You're constructing the BigDecimal from a Double value — a value which is close to 0.58, but isn't exactly.

(FLoats and Doubles are binary floating-point — and almost no decimal fractions have an exact binary representation.  In this case, 0.58 is 0.10010100011110101110000101… in binary.  No Double can store that exactly; the best if can do is store a value very close to it.)

The BigDecimal then does its best to store that value as precisely as possible.  You can see this in the REPL:

>>> java.math.BigDecimal(0.58)
res1: java.math.BigDecimal = 0.57999999999999996003197111349436454474925994873046875

…which makes it obvious why that rounds down to 0.57, not 0.58.

And that holds for all of your problem cases: the nearest Double is slightly less than the decimal value you specify, and so rounds down.  (In the others, it's slightly greater, so those round as expected.  But the underlying problem is still there.)

The best solution is to avoid floating-point binary numbers entirely, and use the BigDecimal(String) constructor:

>>> java.math.BigDecimal("0.58").setScale(2, java.math.RoundingMode.FLOOR)
res6: java.math.BigDecimal! = 0.58

(In this case, of course, there's no longer a need for the setScale() call at all, as the value now has a scale of 2 already!  But it does no harm.)

Upvotes: 2

xinaiz
xinaiz

Reputation: 7788

You fell victim of double floating point precision. When you write

0.62

in reallity it's actually something like

0.6199999999999626787363666....

You should use BigDecimal constructor that takes String instead of Double argument.

Upvotes: 3

Related Questions