Rational ^ Rational

Why does this make a Float64 instead of a Rational?

julia> ((2//1)^(-4//1))
0.0625

Is this the only way to get a Rational out of exponentiation?

julia> Rational((2//1)^(-4//1))
1//16
1 Like

This is the method being called:

^(x::Number, y::Rational) = x^(y.num/y.den)

I suppose the thinking is that you probably don’t want this:

a = 2 // 1
b = -3 // 8

julia> Rational(a ^ b)
6945500098633947//9007199254740992

Rational to the power of rational is typically irrational:

julia> (2//1)^(1//2)
1.4142135623730951
6 Likes

An important principle in Julia to ensure performance, is type stability. I.e. the type of a function’s return value should be a function of the input types. The return type should not vary with the input values, only with their types. Some powers with rational numbers will yield a rational result, but most often it does not. To ensure type stability the Rational{Int64} ^ Rational{Int64} operation returns a Float64. It’s a design choice (it could have been BigFloat or Float32 or whatever, but it is Float64).

1 Like

And note that Rational ^ Integer is a Rational.

julia> ((2//1)^(-4))
1//16
2 Likes

Depending on types you get Float46 (reasonable) or BigFloat (also reasonable), and you can do (but it’s not reasonable):

julia> Rational(big(1//2)^big(1//2))  # scroll to right to see this is still a "Rational", type that is
81877371507464127617551201542979628307507432471243237061821853600756754782485//115792089237316195423570985008687907853269984665640564039457584007913129639936

Rational there implies you get a rational out, but that’s wrong, as the calculated value an irrational number (so I would avoid typing that in code), you only get the most accurate (considering default precision of BigFloat) rational of the resulting BigFloat number, which was already an approximation.

Since you know the result in in general, for a^b, irrational, i.e. none of the approximations are accurate, then this is more reasonable, if you only want some rational approximation:

julia> rationalize(big(1//2)^big(1//2))
4866752642924153522//6882627592338442563

Yes, the type you get is still a Rational, but look at the help string for rationalize, then it’s clear it’s an approximation. I would still just keep the Float64 (or even convert BigFloat to Float64 too).

Or maybe even:

julia> rationalize(Int16, big(1//2)^big(1//2))  # possibly rather Rational{Int64}(rationalize(Int16, big(1//2)^big(1//2)))
13860//19601
1 Like