-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
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
fixed hypot(x::Number...) for under/overflow #27251
Changes from 5 commits
6ccd313
1af76d9
242c94c
6959b71
fef8b1f
74a0a4c
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 | ||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -503,7 +503,46 @@ end | |||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
Compute the hypotenuse ``\\sqrt{\\sum x_i^2}`` avoiding overflow and underflow. | ||||||||||||||||||||||||||||||||||||||||||||||||||||
""" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
hypot(x::Number...) = sqrt(sum(abs2(y) for y in x)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
function hypot(x::Number...) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
xp = promote(x...) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
# compute infnorm xp (modeled on generic_vecnormMinusInf(x) in LinearAlgebra/generic.gl) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
(v, s) = iterate(xp)::Tuple | ||||||||||||||||||||||||||||||||||||||||||||||||||||
maxabs = abs(v) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
while true | ||||||||||||||||||||||||||||||||||||||||||||||||||||
y = iterate(xp, s) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
y === nothing && break | ||||||||||||||||||||||||||||||||||||||||||||||||||||
(v, s) = y | ||||||||||||||||||||||||||||||||||||||||||||||||||||
vnorm = abs(v) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
maxabs = ifelse(isnan(maxabs) | (maxabs > vnorm), maxabs, vnorm) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
end | ||||||||||||||||||||||||||||||||||||||||||||||||||||
maxabsf = float(maxabs) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
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. What is the advantage of this over:
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
# compute vecnorm2(xp) (modeled on generic_vecnorm2(x) in LinearAlgebra/generic.gl) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
(maxabsf == 0 || isinf(maxabsf)) && return maxabsf | ||||||||||||||||||||||||||||||||||||||||||||||||||||
(v, s) = iterate(xp)::Tuple | ||||||||||||||||||||||||||||||||||||||||||||||||||||
T = typeof(maxabsf) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
if isfinite(length(xp)*maxabsf*maxabsf) && maxabsf*maxabsf != 0 # Scaling not necessary | ||||||||||||||||||||||||||||||||||||||||||||||||||||
sum::promote_type(Float64, T) = abs2(v) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
while true | ||||||||||||||||||||||||||||||||||||||||||||||||||||
y = iterate(xp, s) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
y === nothing && break | ||||||||||||||||||||||||||||||||||||||||||||||||||||
(v, s) = y | ||||||||||||||||||||||||||||||||||||||||||||||||||||
sum += abs2(v) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
end | ||||||||||||||||||||||||||||||||||||||||||||||||||||
return convert(T, sqrt(sum)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
else | ||||||||||||||||||||||||||||||||||||||||||||||||||||
sum = (abs(v)/maxabsf)^2 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
while true | ||||||||||||||||||||||||||||||||||||||||||||||||||||
y = iterate(xp, s) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
y === nothing && break | ||||||||||||||||||||||||||||||||||||||||||||||||||||
(v, s) = y | ||||||||||||||||||||||||||||||||||||||||||||||||||||
sum += (abs(v)/maxabsf)^2 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
end | ||||||||||||||||||||||||||||||||||||||||||||||||||||
return convert(T, maxabsf*sqrt(sum)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
end | ||||||||||||||||||||||||||||||||||||||||||||||||||||
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. Similarly, why not just:
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. I copied julia/stdlib/LinearAlgebra/src/generic.jl Lines 298 to 322 in 1d3de4c
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. Unrolling the loops as in the PR is faster and smaller in memory than calling
for code
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. Don't benchmark calls involving a splatted global. Try
etcetera 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. Still a factor of sixteen (I misplaced a decimal when I reported 10^3).
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.
An alternative, if no one likes adding this function to |
||||||||||||||||||||||||||||||||||||||||||||||||||||
end | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
""" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
atan2(y, x) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
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.
Perhaps better to just make the signature
hypot(x::T...) where T<:Number
and then have a separate method forhypot(x::Number...) = hypot(promote(x...))
? Otherwise the compiler has to prove that this is a no-op to eliminate it—which it probably can—but two methods seems clearer anyway.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.
Sounds good. I'll commit a slight revision of that:
hypot(x::Number...) = hypot(promote(x...)...)
correctly passes off tofunction hypot(x::T...) where T<:Number
,whereas
hypot(x::Number...) = hypot(promote(x...))
results inThanks for all the help. I'm learning a lot.
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.
Yes, that's what I meant :)
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.
With the above revisions
make test
fails with the following. I see from NEWS.md that this has to do with "possibly throw[ing]UndefVarError
when accessing static parameters," but I've got no idea how to address it.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.
It seems like the ambiguous case is
hypot()
. What should the hypotenuse of zero numbers be?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.
nothing
?0.0
? Throwing an error seems most sensible to me. That's whatnorm([])
does.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.
In that case the fix would be to have a method that throws an error for zero arguments similar to what norm does.