Reputation: 23815
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
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