Saurabh Gaur
Saurabh Gaur

Reputation: 23815

Confusion using with double value, when groovy decides to make value in double vs BigDecimal?

I'm little bit confused using double value.

When I used as below :-

double foo = 20.46455
assert 20 == foo.round()
assert 20.46 == foo.round(2)

It's working fine. but when I used something like as :-

def foo = 20.46455
assert 20 == foo.round()

it throws :-

java.lang.NullPointerException

and

def foo = 20.46455
assert 20.46 == foo.round(2)

it throws :-

groovy.lang.MissingMethodException: No signature of method: java.math.BigDecimal.round() is applicable for argument types: (java.lang.Integer) values: [2] Possible solutions: round(java.math.MathContext), find(), pow(int), power(java.lang.Integer), find(groovy.lang.Closure), and(java.lang.Number)

It means by default in groovy, value preserve in BigDecimal and BigDecimal.round() expect java.math.MathContext as input.

But My confusion start when I'm using Math.round() which except double as input then why below statement is getting passed while groovy preserve by default in BigDecimal?

def foo = 20.46455
assert 20 == Math.round(foo)

And why I have to use .toDouble() to pass my test case while foo has value in double format as below?

def foo = 20.46455
assert 20 == foo.toDouble().round()
assert 20.46 == foo.toDouble().round(2)

Note :- I don't want to know how to round a double value, I just want to know why groovy behaves differently in each case??

Upvotes: 5

Views: 12677

Answers (1)

Jose Ignacio Acin Pozo
Jose Ignacio Acin Pozo

Reputation: 882

Groovy automatically and implicitly uses BigDecimal for any floating-point numbers unless you define a type or you add a suffix for the number (like D).

Examples:

def foo = 20.46455
println foo.getClass()

Output:

class java.math.BigDecimal

double foo = 20.45645
println foo.getClass()

Output:

class java.lang.Double

def foo = 20.45645d
println foo.getClass()

Output:

class java.lang.Double

Type conversions:

Groovy also has certain automatic type conversions and this is the reason why even though Math.round() only accepts double and float primitives as parameters, the code is not failing when you pass a BigDecimal. To prove this, you could implement your own round function and check what happens with the type conversions:

Examples:

def round(double foo) {
   println foo.getClass()
   return foo.round()
}

def foo = 20.46455
println foo.getClass()
assert 20 == round(foo)

Output:

class java.math.BigDecimal

class java.lang.Double

Some more valid examples of implicit conversions:

def round(float foo) {
   println foo.getClass()
   return foo.round()
}

def foo = 20.46455
println foo.getClass()
assert 20 == round(foo)

Output:

class java.math.BigDecimal

class java.lang.Float

def round(float foo) {
   println foo.getClass()
   return foo.round()
}

def foo = 20
println foo.getClass()
assert 20 == round(foo)

Output:

class java.lang.Integer

class java.lang.Float

def round(double foo) {
   println foo.getClass()
   return foo.round()
}

def foo = 20
println foo.getClass()
assert 20 == round(foo)

Output:

class java.lang.Integer

class java.lang.Double

def round(BigDecimal foo) {   
   println foo.getClass()
   return foo
}

double foo = 20.0
println foo.getClass()
assert 20 == round(foo)

Output:

class java.lang.Double

class java.lang.BigDecimal

As a rule of thumb, if the number is floating point based (double, float, BigDecimal) there will be an implicit type conversion between each other, and the code will throw an exception when trying to convert to non-floating point numbers (like int or long). If a number is not a floating point type (int, long), it can be converted between non-floating and floating point types, as floating point numbers also include non-floating points as a subset (e.g. 1 can be represented with 1.0). This makes sense, as you can't pass the floating point information from a float to an int (20.5 can't be represented with an int variable), but in most cases you can do the opposite, with the occasional exception of overflows for big values (e.g a really big long number into a float variable).

Upvotes: 11

Related Questions