Skip to content
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

Converting large powers of units can result in NaN, Inf, or 0 #647

Closed
Socob opened this issue May 4, 2023 · 0 comments · Fixed by #648
Closed

Converting large powers of units can result in NaN, Inf, or 0 #647

Socob opened this issue May 4, 2023 · 0 comments · Fixed by #648

Comments

@Socob
Copy link
Contributor

Socob commented May 4, 2023

julia> using Unitful

julia> uconvert(u"kb"^10, 1.0 * u"kb"^11 * u"b"^-1)
999.9999999999999 kb^10

julia> uconvert(u"kb"^11, 1.0 * u"kb"^12 * u"b"^-1)
0.0 kb^11

julia> uconvert(u"kb"^12, 1.0 * u"kb"^13 * u"b"^-1)
NaN kb^12

julia> uconvert(u"kOe"^161, 1.0 * u"kOe"^162 * u"Oe"^-1)
1000.0 kOe^161

julia> uconvert(u"kOe"^162, 1.0 * u"kOe"^163 * u"Oe"^-1)
Inf kOe^162

julia> uconvert(u"kOe"^163, 1.0 * u"kOe"^164 * u"Oe"^-1)
NaN kOe^163

Apologies for the contrived examples, but this is only an issue for units with an “inexact” conversion part != 1.0, and becomes more serious the more different it is from 1. Originally, I ran into this in the context of astrophysical units (see JuliaAstro/UnitfulAstro.jl#33), but I wanted to give a more minimal example with “built-in” units.

The issue is that inex^p in basefactor():

Unitful.jl/src/units.jl

Lines 229 to 247 in bd09747

if can_exact
if eq_is_exact
# If we got here then p is an integer.
# Note that sometimes x^1 can cause an overflow error if x is large because
# of how power_by_squaring is implemented for Rationals, so we use dpow.
x = dpow(eq*ex*(10//1)^tens, p)
return (inex^p, isinteger(x) ? Int(x) : x)
else
x = dpow(ex*(10//1)^tens, p)
return ((inex*eq)^p, isinteger(x) ? Int(x) : x)
end
else
if eq_is_exact && can_exact2
x = dpow(eq,p)
return ((inex * ex * 10.0^tens)^p, isinteger(x) ? Int(x) : x)
else
return ((inex * ex * 10.0^tens * eq)^p, 1)
end
end

can result in Inf (inex > 1) or 0.0 (inex < 1) for large p, even though all the large factors end up canceling in the conversion. Ideally, uconvert() would be smarter and avoid dividing these canceling huge/tiny factors, but at a bare minimum, this should give an error instead of silently returning something wrong!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant