Kshitiz Sharma
Kshitiz Sharma

Reputation: 18607

How does type promotion work in Groovy?

Consider the following code snippet -

def factorial(number) {
    if(number == 1)
        return number;
    else 
        return number * factorial(number - 1);
}

println factorial(50)
println factorial(50).getClass()

println()

println 45**20
println ((45**20).getClass())

The output is -

0
class java.lang.Integer

1159445329576199417209625244140625
class java.math.BigInteger

Questions -

  1. Why doesn't groovy automatically promote the result of number * factorial(number-1) to a BigInt in the first case?
  2. Why is the output 0? Why isn't it some random number that we should get after integer overflow?

Upvotes: 1

Views: 185

Answers (2)

madth3
madth3

Reputation: 7344

Old question but I'll try to answer both parts of the question:

Groovy documentation on arithmetic operations states that

binary operations involving subclasses of java.lang.Number automatically convert their arguments according to the following matrix (except for division, which is discussed below)

I won't paste the matrix but it specifies no casting to BigInteger or BigDecimal unless one of the operators is of one of these types.

In the case of division:

The division operators "/" and "/=" produce a Double result if either operand is either Float or Double and a BigDecimal result otherwise

I think that the table is not considering the power operator (**) since it's not present in Java and as stated in @tim_yates comment, power implementation uses BigInteger by default.

The code in DefaultGroovyMethods.java shows clearly that the power of int's is calculated using BigInteger's and if the result is small then is cast down to int again (And that's why (2**4).class is java.lang.Integer):

public static Number power(Integer self, Integer exponent) {
    if (exponent >= 0) {
        BigInteger answer = BigInteger.valueOf(self).pow(exponent);
        if (answer.compareTo(BI_INT_MIN) >= 0 && answer.compareTo(BI_INT_MAX) <= 0) {
            return answer.intValue();
        } else {
            return answer;
        }
    } else {
        return power(self, (double) exponent);
    }
}

To confirm the behaviour of other operations you can go to IntegerMath, LongMath or other classes in the org.codehaus.groovy.runtime.typehandling package

Upvotes: 1

tim_yates
tim_yates

Reputation: 171114

With Groovy, Integer.multiply( Integer ) always returns an Integer.

The factorial method starts overflowing around step 16.

At step 34, you end up with -2147483648 * -2147483648 which returns 0 so the result will always be 0

One fix is to change your method declaration to:

def factorial( BigInteger number ) {

Upvotes: 0

Related Questions