Skip to content

Commit e6d9f01

Browse files
committed
improve handling of underflow for 2x2 eigen
1 parent 01271c9 commit e6d9f01

File tree

1 file changed

+10
-7
lines changed

1 file changed

+10
-7
lines changed

src/eigen.jl

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -157,16 +157,17 @@ end
157157
TA = eltype(A)
158158

159159
@inbounds if A.uplo == 'U'
160-
if !iszero(a[3]) # A is not diagonal
160+
abs2a3 = abs2(a[3])
161+
if !iszero(abs2a3) # A is not diagonal
161162
t_half = real(a[1] + a[4]) / 2
162-
d = real(a[1] * a[4] - a[3]' * a[3]) # Should be real
163+
d = real(a[1] * a[4] - abs2a3) # Should be real
163164

164165
tmp2 = t_half * t_half - d
165166
tmp = tmp2 < 0 ? zero(tmp2) : sqrt(tmp2) # Numerically stable for identity matrices, etc.
166167
vals = SVector(t_half - tmp, t_half + tmp)
167168

168169
v11 = vals[1] - a[4]
169-
n1 = sqrt(v11' * v11 + a[3]' * a[3])
170+
n1 = sqrt(abs2(v11) + abs2a3) # always > 0
170171
v11 = v11 / n1
171172
v12 = a[3]' / n1
172173

@@ -176,16 +177,17 @@ end
176177
return Eigen(vals, vecs)
177178
end
178179
else # A.uplo == 'L'
179-
if !iszero(a[2]) # A is not diagonal
180+
abs2a2 = abs2(a[2])
181+
if !iszero(abs2a2) # A is not diagonal
180182
t_half = real(a[1] + a[4]) / 2
181-
d = real(a[1] * a[4] - a[2]' * a[2]) # Should be real
183+
d = real(a[1] * a[4] - abs2a2) # Should be real
182184

183185
tmp2 = t_half * t_half - d
184186
tmp = tmp2 < 0 ? zero(tmp2) : sqrt(tmp2) # Numerically stable for identity matrices, etc.
185187
vals = SVector(t_half - tmp, t_half + tmp)
186188

187189
v11 = vals[1] - a[4]
188-
n1 = sqrt(v11' * v11 + a[2]' * a[2])
190+
n1 = sqrt(abs2(v11) + abs2a2) # always > 0
189191
v11 = v11 / n1
190192
v12 = a[2] / n1
191193

@@ -196,7 +198,8 @@ end
196198
end
197199
end
198200

199-
# A must be diagonal if we reached this point; treatment of uplo 'L' and 'U' is then identical
201+
# A must be diagonal (or computation of abs2 underflowed) if we reached this point;
202+
# treatment of uplo 'L' and 'U' is then identical
200203
A11 = real(a[1])
201204
A22 = real(a[4])
202205
if A11 < A22

0 commit comments

Comments
 (0)