-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
Commit
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -126,6 +126,7 @@ conj(z::Complex) = complex(real(z),-imag(z)) | |
abs(z::Complex) = hypot(real(z), imag(z)) | ||
abs2(z::Complex) = real(z)*real(z) + imag(z)*imag(z) | ||
inv(z::Complex) = conj(z)/abs2(z) | ||
inv{T<:Integer}(z::Complex{T}) = inv(float(z)) | ||
sign(z::Complex) = z/abs(z) | ||
|
||
(-)(::ImaginaryUnit) = complex(0, -1) | ||
|
@@ -151,6 +152,7 @@ sign(z::Complex) = z/abs(z) | |
*(w::Complex, z::ImaginaryUnit) = complex(-imag(w), real(w)) | ||
|
||
/(z::Number, w::Complex) = z*inv(w) | ||
/(a::Real , w::Complex) = a*inv(w) | ||
/(z::Complex, x::Real) = complex(real(z)/x, imag(z)/x) | ||
|
||
function /(a::Complex, b::Complex) | ||
|
@@ -174,77 +176,68 @@ function /(a::Complex, b::Complex) | |
end | ||
end | ||
|
||
/(a::Real, b::Complex) = | ||
real(convert(promote_type(typeof(a),typeof(b)),a)) / | ||
convert(promote_type(typeof(a),typeof(b)),b) | ||
function /{T<:Real}(a::T, b::Complex{T}) | ||
bre = real(b); bim = imag(b) | ||
if abs(bre) <= abs(bim) | ||
if isinf(bre) && isinf(bim) | ||
r = sign(bre)/sign(bim) | ||
else | ||
r = bre / bim | ||
end | ||
den = bim + r*bre | ||
complex(a*r/den, -a/den) | ||
else | ||
if isinf(bre) && isinf(bim) | ||
r = sign(bim)/sign(bre) | ||
else | ||
r = bim / bre | ||
end | ||
den = bre + r*bim | ||
complex(a/den, -a*r/den) | ||
end | ||
end | ||
function /(a::Complex{Float64}, b::Complex{Float64}) | ||
r,i = robust_cdiv(real(a), imag(a), real(b), imag(b)) | ||
Complex{Float64}(r,i) | ||
end | ||
# robust_cdiv performs complex division in real arithmetic | ||
# the first step is to scale variables if appropriate | ||
# then do calculations in way that avoids over/underflow (subfuncs 1 and 2) | ||
# then undo the scaling | ||
inv{T<:Union(Float16,Float32)}(z::Complex{T}) = | ||
oftype(z, conj(complex128(z))/abs2(complex128(z))) | ||
|
||
# robust complex division for double precision | ||
# the first step is to scale variables if appropriate ,then do calculations | ||
# in a way that avoids over/underflow (subfuncs 1 and 2), then undo the scaling. | ||
# scaling variable s and other techniques | ||
# based on arxiv.1210.4539 | ||
# a + i*b | ||
# p + i*q = --------- | ||
# c + i*d | ||
function robust_cdiv{T<:Float64}(a::T,b::T,c::T,d::T) | ||
const half::T = 0.5 | ||
const two::T = 2.0 | ||
const ab::T = max(abs(a), abs(b)) | ||
const cd::T = max(abs(c), abs(d)) | ||
const ov::T = realmax(T) | ||
const un::T = realmin(T) | ||
const ϵ::T = eps(T) | ||
const bs=two/(ϵ*ϵ) | ||
s::T = 1.0 | ||
function /(z::Complex128, w::Complex128) | ||
a, b = reim(z); c, d = reim(w) | ||
half = 0.5 | ||
two = 2.0 | ||
ab = max(abs(a), abs(b)) | ||
cd = max(abs(c), abs(d)) | ||
ov = realmax(a) | ||
un = realmin(a) | ||
ϵ = eps(Float64) | ||
bs = two/(ϵ*ϵ) | ||
s = 1.0 | ||
ab >= half*ov && (a=half*a; b=half*b; s=two*s ) # scale down a,b | ||
cd >= half*ov && (c=half*c; d=half*d; s=s*half) # scale down c,d | ||
ab <= un*two/ϵ && (a=a*bs; b=b*bs; s=s/bs ) # scale up a,b | ||
cd <= un*two/ϵ && (c=c*bs; d=d*bs; s=s*bs ) # scale up c,d | ||
abs(d)<=abs(c) ? ((p,q)=robust_cdiv1(a,b,c,d) ) : ((p,q)=robust_cdiv1(b,a,d,c); q=-q) | ||
return p*s,q*s # undo scaling | ||
end | ||
function robust_cdiv1{T<:Float64}(a::T,b::T,c::T,d::T) | ||
const one::T = 1.0 | ||
const r::T=d/c | ||
const t::T=one/(c+d*r) | ||
p::T=robust_cdiv2(a,b,c,d,r,t) | ||
q::T=robust_cdiv2(b,-a,c,d,r,t) | ||
return Complex128(p*s,q*s) # undo scaling | ||
end | ||
function robust_cdiv1(a::Float64, b::Float64, c::Float64, d::Float64) | ||
This comment has been minimized.
Sorry, something went wrong. |
||
r = d/c | ||
t = 1.0/(c+d*r) | ||
p = robust_cdiv2(a,b,c,d,r,t) | ||
q = robust_cdiv2(b,-a,c,d,r,t) | ||
return p,q | ||
end | ||
function robust_cdiv2{T<:Float64}(a::T,b::T,c::T,d::T,r::T,t::T) | ||
const zero::T = 0.0 | ||
if r != zero | ||
const br::T = b*r | ||
return (br != zero ? (a+br)*t : a*t + (b*t)*r) | ||
function robust_cdiv2(a::Float64, b::Float64, c::Float64, d::Float64, r::Float64, t::Float64) | ||
if r != 0 | ||
br = b*r | ||
return (br != 0 ? (a+br)*t : a*t + (b*t)*r) | ||
else | ||
return (a + d*(b/c)) * t | ||
end | ||
end | ||
|
||
|
||
function inv(w::Complex128) | ||
c, d = reim(w) | ||
half = 0.5 | ||
two = 2.0 | ||
cd = max(abs(c), abs(d)) | ||
ov = realmax(c) | ||
un = realmin(c) | ||
ϵ = eps(Float64) | ||
bs = two/(ϵ*ϵ) | ||
s = 1.0 | ||
cd >= half*ov && (c=half*c; d=half*d; s=s*half) # scale down c,d | ||
cd <= un*two/ϵ && (c=c*bs; d=d*bs; s=s*bs ) # scale up c,d | ||
abs(d)<=abs(c) ? ((p,q)=robust_cdiv1(1.0,0.0,c,d)) : | ||
This comment has been minimized.
Sorry, something went wrong.
stevengj
Member
|
||
((p,q)=robust_cdiv1(0.0,1.0,d,c); q=-q) | ||
return Complex128(p*s,q*s) # undo scaling | ||
end | ||
|
||
function ssqs{T<:FloatingPoint}(x::T, y::T) | ||
k::Int = 0 | ||
ρ = x*x + y*y | ||
|
6 comments
on commit bdd0c7e
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.
I think there's a copysign missing somewhere.
julia> inv(1e300+1e-20im) #correct sign
1.0e-300 - 0.0im
julia> inv(1e300+1e-30im) #wrong sign
1.0e-300 + 0.0im
julia> inv(1e300+0.0im) #wrong sign
1.0e-300 + 0.0im
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.
@jiahao's comment, it also seems like the test should use ===
in order to check the sign of zero.
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.
And should inv(complex(0.0))
give complex(Inf,-0.0)
?
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.
inv(complex(0.0))
should probably throw a DomainError
, as should all the other complex(+/-0, +/-0)
s.
Float zeros are actually limits of infinitesimals going to +/-0, so you can work this out as the limit of 1/z where z = ε + i ε', taking ε -> +0 and ε'-> +0. This is the same as taking the limit of (ε - iε')/(ε^2 + ε'^2). The problem that arises is that the answer you get depends on which limit you take first. If you take ε->0 first and ε'->0 second, you get complex(+Inf, -0.0)
as the answer, but if you take ε'->0 first and ε->0 second, you get complex(+0, -Inf)
. So complex(+0.0, +0.0)
does not map analytically onto a unique limiting point in the Complex
plane, which is one of those mathematically annoying differences between Real
and Complex
. (Mathematica gets around this by returning ComplexInfinity
, but we don't have this. Not yet, at least.)
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.
ComplexInfinity is not a good choice since it would wreck type stability. If we were working with a PolarComplex representation, then it would be easy to represent specific complex infinities (infinite magnitude, finite theta).
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.
Just to clarify, I wasn't advocating ComplexInfinity... and PolarComplex doesn't help you in this case either, because you can do the limit in polar form to see that the phase isn't unique.
Slightly more descriptive parameter names would help here. Even
r1
,i1
andr2
,i2
would at least indicate their meaning.