Unitful.jl icon indicating copy to clipboard operation
Unitful.jl copied to clipboard

Conversion to Float64

Open cstjean opened this issue 7 years ago • 10 comments
trafficstars

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?

cstjean avatar Nov 08 '18 17:11 cstjean

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).

ajkeller34 avatar Nov 08 '18 18:11 ajkeller34

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.

cstjean avatar Nov 08 '18 18:11 cstjean

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.

ajkeller34 avatar Nov 08 '18 19:11 ajkeller34

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.

cstjean avatar Nov 08 '18 19:11 cstjean

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?

PharmCat avatar Sep 05 '22 07:09 PharmCat

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

sostock avatar Oct 31 '22 08:10 sostock

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)

PharmCat avatar Oct 31 '22 15:10 PharmCat

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.

sostock avatar Oct 31 '22 15:10 sostock

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.

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)

PharmCat avatar Oct 31 '22 20:10 PharmCat

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)

sostock avatar Nov 02 '22 11:11 sostock