Unitful.jl
Unitful.jl copied to clipboard
Conversion to Float64
Unit conversions sometimes convert Float32 to Float64
julia> uconvert(u"°C", 1.0f0 * u"°F")
-17.222198f0 °C # stay in Floa32
julia> uconvert(u"mbar", 1.0f0 * u"psi")
68.94757293168361 mbar # Float64
I guess I should just deal with it on my side? It feels a bit gross to special-case Float32 in uconvert. What do you think?
I think it should only happen if the unit-conversion factor works out to be a Float64. Rational{Int} conversion factors (defined in pkgdefaults.jl) shouldn't do this. The easiest would be to tweak pkgdefaults.jl to avoid these situations where possible (the pound--kg conversion for instance).
Right, but in this case, gn would still be a Float64. Since it's exact, it could be a ratio too?
const gn = 9.80665*m/s^2 # exact, standard acceleration of gravity
The way Base handles pi is interesting. It promotes to Floa32 if the other number is a Float32:
julia> 2.0*pi
6.283185307179586
julia> 2.0f0*pi
6.2831855f0
It's defined as:
Base.@irrational π 3.14159265358979323846 pi
I can't think of a simple way of bringing those mechanics to Unitful, unfortunately.
Defining gn as a ratio seems legitimate. In the short term perhaps that would solve your problem?
One could probably define conversion constants as Quantity{Irrational{:something}, ...}. There has been talk of defining physical constants in such a way for similar reasons in other issues.
Even if the original constants are Irrational, derived conversion factors like @unit lbf "lbf" PoundsForce 1lb*ge false will be Float64. We'd have to have convfact return an Irrational. At which point, it might be easier to just special-case uconvert for Float32 quantities.
This is enother question:
Why convert(Float64, 1.0u"μm/m") work fine, but convert(Float64, 1.0u"ng/ml") doesn't work:
ERROR: DimensionError: and ng mL^-1 are not dimensionally compatible.
Stacktrace:
[1] #s81#159
@ C:\Users\$\.julia\packages\Unitful\SUQzL\src\conversion.jl:12 [inlined]
[2] var"#s81#159"(::Any, s::Any, t::Any)
@ Unitful .\none:0
[3] (::Core.GeneratedFunctionStub)(::Any, ::Vararg{Any})
@ Core .\boot.jl:580
[4] uconvert(a::Unitful.FreeUnits{(), NoDims, nothing}, x::Quantity{Float64, 𝐌 𝐋^-3, Unitful.FreeUnits{(ng, mL^-1), 𝐌 𝐋^-3, nothing}} )
@ Unitful C:\Users\$\.julia\packages\Unitful\SUQzL\src\conversion.jl:78
[5] convert(#unused#::Type{Float64}, y::Quantity{Float64, 𝐌 𝐋^-3, Unitful.FreeUnits{(ng, mL^-1), 𝐌 𝐋^-3, nothing}} )
@ Unitful C:\Users\$\.julia\packages\Unitful\SUQzL\src\conversion.jl:145
[6] top-level scope
@ none:1
update:
Seems I understand why...
But is it possible to get float values with methods ∈ Base?
But is it possible to get float values with methods ∈ Base?
By “get float values”, do you mean stripping the units? That is, you want to convert 1.0u"ng/ml" into 1.0?
You can achieve this by only using Base functions (i.e., without calling ustrip):
julia> x = 1.0u"ng/ml"
1.0 ng mL^-1
julia> x / oneunit(x)
1.0
Thank you so much! Also I try to represent vector of units as the values. Is this approach is correct?
uvec = ones(typeof(1u"mg"), 10)
T = typeof(1u"mg")
vec = reinterpret(typeof(one(T)), uvec)
It will work if uvec is constructed like this, but for a general vector it might not. Basically, for this to work, the eltype of uvec must be a concrete subtype of Quantity{T,D,U} where T and U are concrete types as well. Other AbstractQuantity types might not work, and vectors with non-concrete eltype will also not.
It will work if
uvecis constructed like this, but for a general vector it might not. Basically, for this to work, theeltypeofuvecmust be a concrete subtype ofQuantity{T,D,U}whereTandUare concrete types as well. OtherAbstractQuantitytypes might not work, and vectors with non-concrete eltype will also not.
Thank you again!
So, better use:
uvec = ones(typeof(1u"mg"), 10)
vec = uvec ./ oneunit(eltype(uvec ))
or
uvec = ones(typeof(1u"mg"), 10)
@. vec = uvec / oneunit(uvec)
vec = uvec ./ oneunit(eltype(uvec ))
This will not work for vectors whose eltype is not concrete:
julia> uvec = [1u"m", 1u"s"]
2-element Vector{Quantity{Int64}}:
1 m
1 s
julia> vec = uvec ./ oneunit(eltype(uvec))
ERROR: MethodError: no method matching (Quantity{Int64})(::Int64)
[...]
@. vec = uvec / oneunit(uvec)
This will work, but vec must already exist and have the right size. If it doesn’t, you need to move the @. to the right of the =:
vec = @. uvec / oneunit(uvec)