Skip to content

Commit 90d0151

Browse files
committed
improve handling of underflow for 2x2 eigen
1 parent 1b18757 commit 90d0151

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
@@ -136,16 +136,17 @@ end
136136
TA = eltype(A)
137137

138138
@inbounds if A.uplo == 'U'
139-
if !iszero(a[3]) # A is not diagonal
139+
abs2a3 = abs2(a[3])
140+
if !iszero(abs2a3) # A is not diagonal
140141
t_half = real(a[1] + a[4]) / 2
141-
d = real(a[1] * a[4] - a[3]' * a[3]) # Should be real
142+
d = real(a[1] * a[4] - abs2a3) # Should be real
142143

143144
tmp2 = t_half * t_half - d
144145
tmp = tmp2 < 0 ? zero(tmp2) : sqrt(tmp2) # Numerically stable for identity matrices, etc.
145146
vals = SVector(t_half - tmp, t_half + tmp)
146147

147148
v11 = vals[1] - a[4]
148-
n1 = sqrt(v11' * v11 + a[3]' * a[3])
149+
n1 = sqrt(abs2(v11) + abs2a3) # always > 0
149150
v11 = v11 / n1
150151
v12 = a[3]' / n1
151152

@@ -155,16 +156,17 @@ end
155156
return (vals, vecs)
156157
end
157158
else # A.uplo == 'L'
158-
if !iszero(a[2]) # A is not diagonal
159+
abs2a2 = abs2(a[2])
160+
if !iszero(abs2a2) # A is not diagonal
159161
t_half = real(a[1] + a[4]) / 2
160-
d = real(a[1] * a[4] - a[2]' * a[2]) # Should be real
162+
d = real(a[1] * a[4] - abs2a2) # Should be real
161163

162164
tmp2 = t_half * t_half - d
163165
tmp = tmp2 < 0 ? zero(tmp2) : sqrt(tmp2) # Numerically stable for identity matrices, etc.
164166
vals = SVector(t_half - tmp, t_half + tmp)
165167

166168
v11 = vals[1] - a[4]
167-
n1 = sqrt(v11' * v11 + a[2]' * a[2])
169+
n1 = sqrt(abs2(v11) + abs2a2) # always > 0
168170
v11 = v11 / n1
169171
v12 = a[2] / n1
170172

@@ -175,7 +177,8 @@ end
175177
end
176178
end
177179

178-
# A must be diagonal if we reached this point; treatment of uplo 'L' and 'U' is then identical
180+
# A must be diagonal (or computation of abs2 underflowed) if we reached this point;
181+
# treatment of uplo 'L' and 'U' is then identical
179182
A11 = real(a[1])
180183
A22 = real(a[4])
181184
if A11 < A22

0 commit comments

Comments
 (0)