Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

java.math.BigDecimal division inaccuracy

In Kotlin, BigDecimal division returns strange results.

BigDecimal(1.0) / BigDecimal(2.0)
// returns 0.0
// Just like Int(1) / Int(2) returns 0

But:

BigDecimal(1.1) / BigDecimal(2.0)
// returns 0.55
// Just like Double(1.1) / Double(2.0) returns 0.55

So why does that happen and how can I make 1.0 / 2.0 returns 0.5 with BigDecimal without using divide() giving a scale parameter?

UPDATES:

Here are the complete codes:

Welcome to Kotlin version 1.2.41 (JRE 1.8.0_172-b11)
Type :help for help, :quit for quit
>>> import java.math.BigDecimal
>>> BigDecimal(1.0) / BigDecimal(2.0)
0
>>> BigDecimal(1.1) / BigDecimal(2.0)
0.550000000000000044408920985006261616945266723632812

UPDATES 2:

According to @user2864740

BigDecimal(1.0).divide(BigDecimal(2.0))
// returns 0.5

and

BigDecimal("1.0") / BigDecimal(2.0)
// returns 0.5

So the results are confusing. For BigDecimals, why do 1.0 / 2.0 returns 0.0, 1.0.divide(2.0) returns 0.5, and "1.0" / 2.0 returns 0.5?

like image 824
user3254156 Avatar asked Aug 07 '18 04:08

user3254156


People also ask

How accurate is BigDecimal?

This limits it to 15 to 17 decimal digits of accuracy. BigDecimal can grow to any size you need it to. Double operates in binary which means it can only precisely represent numbers which can be expressed as a finite number in binary. For example, 0.375 in binary is exactly 0.011.

Which is more accurate BigDecimal or double?

A BigDecimal is an exact way of representing numbers. A Double has a certain precision. Working with doubles of various magnitudes (say d1=1000.0 and d2=0.001 ) could result in the 0.001 being dropped alltogether when summing as the difference in magnitude is so large. With BigDecimal this would not happen.

How do I stop rounding in BigDecimal?

56 if you use BigDecimal newValue = myBigDecimal. setScale(2, RoundingMode. DOWN); " This statement is true for HALF_DOWN not for DOWN mode.

Is Java BigDecimal immutable?

Since BigDecimal is immutable, these operations do not modify the existing objects. Rather, they return new objects.


2 Answers

If you write divide explicitly, it will work the same way as it does in Java:

val a = BigDecimal(1.0)
val b = BigDecimal(2.0)
val x = a.divide(b) // 0.5

When you use the / operator, that translates to the BigDecimal.div extension from the standard library, which does the following:

Enables the use of the / operator for BigDecimal instances.

The scale of the result is the same as the scale of this (divident), and for rounding the RoundingMode.HALF_EVEN rounding mode is used.

You can take a look at how the different RoundingMode constants work here.

like image 103
zsmb13 Avatar answered Oct 09 '22 02:10

zsmb13


1.1 is a double literal

A floating-point literal is of type float if it ends with the letter F or f; otherwise its type is double and it can optionally end with the letter D or d.

https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html

Therefore by the time the value is passed to BigDecimal you already lost precision due to the double value. BigDecimal(1.1) is equivalent to double d = 1.1; BigDecimal(d). You need to pass the value as string

BigDecimal("1.1")

unless the value is exactly representable as a double like 1.5

like image 36
phuclv Avatar answered Oct 09 '22 02:10

phuclv