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

Preserve the floating-point precision of quantities in uconvert #754

Merged
merged 11 commits into from
Dec 20, 2024
20 changes: 19 additions & 1 deletion src/conversion.jl
Original file line number Diff line number Diff line change
@@ -1,3 +1,21 @@
"""
UnitConversionFactor(x::AbstractFloat)
Conversion factor with value `x`.

Used by the [`convfact`](@ref) function in floating point conversion factors
to preserve the precision of quantities.
"""
struct UnitConversionFactor{T<:AbstractFloat} <: AbstractIrrational
x::T
UnitConversionFactor(x::T) where {T<:AbstractFloat} = new{T}(x)
eliascarv marked this conversation as resolved.
Show resolved Hide resolved
end

eliascarv marked this conversation as resolved.
Show resolved Hide resolved
Base.:(==)(a::UnitConversionFactor, b::UnitConversionFactor) = a.x == b.x
Base.hash(x::UnitConversionFactor, h::UInt) = hash(x.x, h)
Base.BigFloat(x::UnitConversionFactor) = BigFloat(x.x)
Base.Float64(x::UnitConversionFactor) = Float64(x.x)
Base.Float32(x::UnitConversionFactor) = Float32(x.x)

"""
convfact(s::Units, t::Units)
Find the conversion factor from unit `t` to unit `s`, e.g., `convfact(m, cm) == 1//100`.
Expand Down Expand Up @@ -37,7 +55,7 @@ Find the conversion factor from unit `t` to unit `s`, e.g., `convfact(m, cm) ==
"exponents and/or SI prefixes in units"
))
end
return :($result)
return :($(result isa AbstractFloat ? UnitConversionFactor(result) : result))
eliascarv marked this conversation as resolved.
Show resolved Hide resolved
end

"""
Expand Down
18 changes: 18 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,24 @@ end
# Issue 647:
@test uconvert(u"kb^1000", 1u"kb^1001 * b^-1") === 1000u"kb^1000"
@test uconvert(u"kOe^1000", 1u"kOe^1001 * Oe^-1") === 1000u"kOe^1000"
# Issue 753:
# avoid converting the float type of quantities
@test Unitful.numtype(uconvert(m, BigFloat(100)cm)) === BigFloat
@test Unitful.numtype(uconvert(cm, (BigFloat(1)π + im) * m)) === Complex{BigFloat}
@test Unitful.numtype(uconvert(rad, BigFloat(360)°)) === BigFloat
@test Unitful.numtype(uconvert(°, (BigFloat(2)π + im) * rad)) === Complex{BigFloat}
@test Unitful.numtype(uconvert(m, 100.0cm)) === Float64
@test Unitful.numtype(uconvert(cm, (1.0π + im) * m)) === ComplexF64
@test Unitful.numtype(uconvert(rad, 360.0°)) === Float64
@test Unitful.numtype(uconvert(°, (2.0π + im) * rad)) === ComplexF64
@test Unitful.numtype(uconvert(m, 100f0cm)) === Float32
eliascarv marked this conversation as resolved.
Show resolved Hide resolved
@test Unitful.numtype(uconvert(cm, (1f0π + im) * m)) === ComplexF32
@test Unitful.numtype(uconvert(rad, 360f0°)) === Float32
@test Unitful.numtype(uconvert(°, (2f0π + im) * rad)) === ComplexF32
@test Unitful.numtype(uconvert(m, Float16(100)cm)) === Float16
@test Unitful.numtype(uconvert(cm, (Float16(1)π + im) * m)) === ComplexF16
@test Unitful.numtype(uconvert(rad, Float16(360)°)) === Float16
@test Unitful.numtype(uconvert(°, (Float16(2)π + im) * rad)) === ComplexF16
# Floating point overflow/underflow in uconvert can happen if the
# conversion factor is large, because uconvert does not cancel
# common basefactors (or just for really large exponents and/or
Expand Down
Loading