# Lossless type promotion

What is the correct, generic way to figure out if values of a type `T` can be converted into another type `S` without loss of precision?

`Base.typejoin` cannot be used for this (eg for `(Int8, Int16)`, it returns `Signed`). `promote_type` would allow `Int64` to `Float64`, which is not always lossless.

I want something like

``````mypromote(Int8, Int64) == Int64
mypromote(Int32, Float64) == Float64
mypromote(Int64, Float64) == Real
``````

but I wonder if I can implement it without a lot of special cases.

To me it seems you could write something that would cover all of the “machine numbers” using `sizeof`, testing whether a type is signed, and testing whether it’s `AbstractFloat` or `Integer`. You’d also have to implement a lookup involving these three values to return an appropriate type, and hence may need to mark it as `Base.@pure`. (Though with constant-propagation, who knows?)

Of course this won’t cover the whole zoo of number types that Julia now supports (complex numbers, dual numbers, physical units, intervals, etc.). Being generic on that scale is a much harder problem.

1 Like

What he said.

``````import Base: promote_rule

for I in (:Int8, :Int16, :Int32, :Int64, :Int128)
for F in (:Float16, :Float32, :Float64)
@eval begin
if sizeof(\$I) < sizeof(\$F)
promote_rule(::Type{\$I}, ::Type{\$F}) = \$F
elseif sizeof(\$I) < sizeof(widen(\$F))
promote_rule(::Type{\$I}, ::Type{\$F}) = widen(\$F)
elseif sizeof(\$I) < sizeof(widen(widen(\$F)))
promote_rule(::Type{\$I}, ::Type{\$F}) = widen(widen(\$F))
else
promote_rule(::Type{\$I}, ::Type{\$F}) = BigFloat
end
end
end
end
``````