-
-
Notifications
You must be signed in to change notification settings - Fork 5.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Faster, more correct complex^complex #24570
Changes from 9 commits
2a6172a
a72f88a
25f57c6
afc3377
a094e23
3c2ffbd
8d67731
f0cd0c2
0127a4c
e2fb636
10e11a5
b83e3a5
cb1a52c
ea9b4a4
f78e032
90f2bc5
c575212
28d9c0d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -648,98 +648,73 @@ end | |
exp10(z::Complex) = exp10(float(z)) | ||
|
||
# _cpow helper function to avoid method ambiguity with ^(::Complex,::Real) | ||
function _cpow(z::Complex{T}, p::Union{T,Complex{T}})::Complex{T} where T<:AbstractFloat | ||
if p == 2 #square | ||
zr, zi = reim(z) | ||
x = (zr-zi)*(zr+zi) | ||
y = 2*zr*zi | ||
if isnan(x) | ||
if isinf(y) | ||
x = copysign(zero(T),zr) | ||
elseif isinf(zi) | ||
x = convert(T,-Inf) | ||
elseif isinf(zr) | ||
x = convert(T,Inf) | ||
function _cpow(z::Union{T,Complex{T}}, p::Union{T,Complex{T}}) where {T<:AbstractFloat} | ||
if isreal(p) | ||
pᵣ = real(p) | ||
if isinteger(pᵣ) && abs(pᵣ) < typemax(Int32) | ||
# |p| < typemax(Int32) serves two purposes: it prevents overflow | ||
# when converting p to Int, and it also turns out to be roughly | ||
# the crossover point for exp(p*log(z)) or similar to be faster. | ||
if iszero(pᵣ) # fix signs of imaginary part for z^0 | ||
zer = flipsign(copysign(zero(T),pᵣ), imag(z)) | ||
return Complex(one(T), zer) | ||
end | ||
elseif isnan(y) && isinf(x) | ||
y = copysign(zero(T), y) | ||
end | ||
Complex(x,y) | ||
elseif z!=0 | ||
if p!=0 && isinteger(p) | ||
rp = real(p) | ||
if rp < 0 | ||
return power_by_squaring(inv(z), convert(Integer, -rp)) | ||
ip = convert(Int, pᵣ) | ||
if isreal(z) | ||
zᵣ = real(z) | ||
if ip < 0 | ||
iszero(z) && return Complex(T(NaN),T(NaN)) | ||
re = power_by_squaring(inv(zᵣ), -ip) | ||
im = -imag(z) | ||
else | ||
re = power_by_squaring(zᵣ, ip) | ||
im = imag(z) | ||
end | ||
# slightly tricky to get the correct sign of zero imag. part | ||
return Complex(re, ifelse(iseven(ip) & signbit(zᵣ), -im, im)) | ||
else | ||
return power_by_squaring(z, convert(Integer, rp)) | ||
return ip < 0 ? power_by_squaring(inv(z), -ip) : power_by_squaring(z, ip) | ||
end | ||
elseif isreal(z) | ||
# (note: if both z and p are complex with ±0.0 imaginary parts, | ||
# the sign of the ±0.0 imaginary part of the result is ambiguous) | ||
if iszero(real(z)) | ||
return pᵣ > 0 ? complex(z) : Complex(T(NaN),T(NaN)) # 0 or NaN+NaN*im | ||
elseif real(z) > 0 | ||
return Complex(real(z)^pᵣ, z isa Real ? ifelse(real(z) < 1, -imag(p), imag(p)) : flipsign(imag(z), pᵣ)) | ||
else | ||
zᵣ = real(z) | ||
rᵖ = (-zᵣ)^pᵣ | ||
# figuring out the sign of 0.0 when p is a complex number | ||
# with zero imaginary part and integer/2 real part could be | ||
# improved here, but it's not clear if it's worth it… | ||
return rᵖ * complex(cospi(pᵣ), flipsign(sinpi(pᵣ),imag(z))) | ||
end | ||
end | ||
exp(p*log(z)) | ||
elseif p!=0 #0^p | ||
zero(z) #CHECK SIGNS | ||
else #0^0 | ||
zer = copysign(zero(T),real(p))*copysign(zero(T),imag(z)) | ||
Complex(one(T), zer) | ||
end | ||
end | ||
function _cpow(z::Complex{T}, p::Union{T,Complex{T}}) where T<:Real | ||
if isinteger(p) | ||
rp = real(p) | ||
if rp < 0 | ||
return power_by_squaring(inv(float(z)), convert(Integer, -rp)) | ||
else | ||
return power_by_squaring(float(z), convert(Integer, rp)) | ||
end | ||
end | ||
pr, pim = reim(p) | ||
zr, zi = reim(z) | ||
r = abs(z) | ||
rp = r^pr | ||
theta = atan2(zi, zr) | ||
ntheta = pr*theta | ||
if pim != 0 && r != 0 | ||
rp = rp*exp(-pim*theta) | ||
ntheta = ntheta + pim*log(r) | ||
end | ||
sinntheta, cosntheta = sincos(ntheta) | ||
re, im = rp*cosntheta, rp*sinntheta | ||
if isinf(rp) | ||
if isnan(re) | ||
re = copysign(zero(re), cosntheta) | ||
end | ||
if isnan(im) | ||
im = copysign(zero(im), sinntheta) | ||
return abs(z)^pᵣ * cis(pᵣ*angle(z)) | ||
end | ||
end | ||
|
||
# apply some corrections to force known zeros | ||
if pim == 0 | ||
if isinteger(pr) | ||
if zi == 0 | ||
im = copysign(zero(im), im) | ||
elseif zr == 0 | ||
if isinteger(0.5*pr) # pr is even | ||
im = copysign(zero(im), im) | ||
else | ||
re = copysign(zero(re), re) | ||
end | ||
end | ||
elseif isreal(z) | ||
iszero(z) && return real(p) > 0 ? complex(z) : Complex(T(NaN),T(NaN)) # 0 or NaN+NaN*im | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @StefanKarpinski, is the policy these days to throw I'm a little confused because it doesn't seem like #5234 was ever resolved. |
||
zᵣ = real(z) | ||
pᵣ, pᵢ = reim(p) | ||
if zᵣ > 0 | ||
return zᵣ^pᵣ * cis(pᵢ*log(zᵣ)) | ||
else | ||
dr = pr*2 | ||
if isinteger(dr) && zi == 0 | ||
if zr < 0 | ||
re = copysign(zero(re), re) | ||
else | ||
im = copysign(zero(im), im) | ||
end | ||
end | ||
r = -zᵣ | ||
θ = copysign(T(π),imag(z)) | ||
return (r^pᵣ * exp(-pᵢ*θ)) * cis(pᵣ*θ + pᵢ*log(r)) | ||
end | ||
else | ||
pᵣ, pᵢ = reim(p) | ||
r = abs(z) | ||
θ = angle(z) | ||
return (r^pᵣ * exp(-pᵢ*θ)) * cis(pᵣ*θ + pᵢ*log(r)) | ||
end | ||
|
||
Complex(re, im) | ||
end | ||
_cpow(z, p) = _cpow(float(z), float(p)) | ||
^(z::Complex{T}, p::Complex{T}) where T<:Real = _cpow(z, p) | ||
^(z::Complex{T}, p::T) where T<:Real = _cpow(z, p) | ||
^(z::T, p::Complex{T}) where T<:Real = _cpow(z, p) | ||
|
||
^(z::Complex, n::Bool) = n ? z : one(z) | ||
^(z::Complex, n::Integer) = z^Complex(n) | ||
|
@@ -755,6 +730,10 @@ function ^(z::Complex{T}, p::S) where {T<:Real,S<:Real} | |
P = promote_type(T,S) | ||
return Complex{P}(z) ^ P(p) | ||
end | ||
function ^(z::T, p::Complex{S}) where {T<:Real,S<:Real} | ||
P = promote_type(T,S) | ||
return P(z) ^ Complex{P}(p) | ||
end | ||
|
||
function sin(z::Complex{T}) where T | ||
F = float(T) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe write it as
exp2(31)
instead? It is jarring to see integer types in here.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But typemax(Int32) is literally what we want to prevent overflow on 32-bit machines, and to have consistent behavior on 64-bit. The whole point of this
if
statement is to convert to an integer type.