I’ve been playing with the @code_typed macro and checking how some pieces of code behave. I was trying to check the differences between ^(2,5) and ^(2,-5) but @code_typed shows the same output for these two cases (I’m using Julia 1.6.1):
Using @which ^(2,5), I was able to find where the implementation of this function is:
julia> @which ^(2,5)
^(x::T, p::T) where T<:Integer in Base at intfuncs.jl:289
I found out that it calls power_by_squaring and that if the exponent is negative and the base is different from 1 or -1, it should throw an error:
function power_by_squaring(x_, p::Integer)
x = to_power_type(x_)
if p == 1
return copy(x)
elseif p == 0
return one(x)
elseif p == 2
return x*x
elseif p < 0
isone(x) && return copy(x)
isone(-x) && return iseven(p) ? one(x) : copy(x)
throw_domerr_powbysq(x, p)
end
t = trailing_zeros(p) + 1
p >>= t
while (t -= 1) > 0
x *= x
end
y = x
while p > 0
t = trailing_zeros(p) + 1
p >>= t
while (t -= 1) >= 0
x *= x
end
y *= x
end
return y
end
^(x::T, p::T) where {T<:Integer} = power_by_squaring(x,p)
So I’m left with two unanswered questions:
Why, in @code_typed, the return type of ^(2,-5) is Int64 and not Float64?
I think the @code... -macros are wrong here. 2^5 (and 2^-5) actually lowers to Base.literal_pow(^, 2, Val(5)). If in the REPL you do a @less 2^-5, you see the comment:
# x^p for any literal integer p is lowered to Base.literal_pow(^, x, Val(p))
# to enable compile-time optimizations specialized to p.
This has a somewhat weird effect:
julia> 2^-5
0.03125
julia> p = -5
-5
julia> 2^p
ERROR: DomainError with -5:
Cannot raise an integer x to a negative power -5.
Make x or -5 a float by adding a zero decimal (e.g., 2.0^-5 or 2^-5.0 instead of 2^-5)or write 1/x^5, float(x)^-5, x^float(-5) or (x//1)^-5.
Stacktrace:
...