# Convert to integer type of minimum required size

I thought that this should be a trivial question, but I’m failing to find the answer. How can I convert an arbitrary value for which `isinteger(x)` is `true`, into the subtype of `Signed` or `Unsigned` with the minimum required size?

For instance, if I want a `Signed` this should be `Int64` for `2e10`, `Int128` for `2e19`, etc.

I hoped that the `Signed` constructor would do this, but it seems to be equivalent to `Int` (and `Unsigned` seems to be the same as `UInt`), so it doesn’t work.

For floating point numbers, there is the `float` function that converts any number to an appropriate subtype of `AbstractFloat`, but I didn’t find an equivalent solution for integers.

Doing a conversion like this is type-unstable (because it depends on the value, not the type), so you wouldn’t normally want to do it in performance-sensitive code. And if you have performance-insensitive code, you might as well just use `BigInt`.

That being said, you could certainly do something like this in principle:

``````function tointeger(x::AbstractFloat)
for T in (Int8, Int16, Int32, Int64, Int128)
if typemin(T) ≤ x ≤ typemax(T)
return T(x)
end
end
return BigInt(x)
end
``````

But as I said, doing type-unstable conversions like this will result in slow code.

Why do you want to do this?

2 Likes

Well, I do not know why heliosdrm had to it, but it is certainly necessary sometimes. As pointed in previous discussions, if one wants an exact determinant of an integer matrix, one has often to convert
the matrix to `BigInt`. If the determinant happens to be a normal integer, then converting it to `Int`
will certainly speed up the rest of the computation…

Actually nothing serious; I was just experimenting with conversions between numeric types, and noticed this lack of an equivalent to `float`, which as pointed out by @Jean_Michel might be convenient in some situations. But the problem of type stability makes a lot of sense of course, so I understand that the advice is: for arbitrary size just use `BigInt` (well, or SafeIntegers maybe).

Yet, looking into the precision limits of different types, I can see that there are some constraints that can be used to define type-stable (partial) solutions to this problem:

• If a number is an integer approximated in a `Float16`, it can always be represented as an `Int32`, since `Float16(typemax(Int32)) == Inf16` (and for `typemin` it is `-Inf16`).
• Integer approximations in `Float32` or higher can only be expressed in full by `BigInt`.
• Exact integers represented by `Float16` (smaller than `maxintfloat(Float16)`) can be represented by `Int16`; and same for `Float32`/`Int32`, and `Float64`/`Int64`.

`Float16(6.5504e4) == 65504` is an exact integer. `maxintfloat(Float16)` is only the largest consecutive integer that is exactly represented in `Float16`.

2 Likes

You mean `UInt16` right? Because:

``````julia> trunc(Int16, prevfloat(typemax(Float16)))
ERROR: InexactError: trunc(Int16, 65504.0)
Stacktrace:
 trunc at ./float.jl:682 [inlined]
 trunc(::Type{Int16}, ::Float16) at ./float.jl:360
 top-level scope at REPL:1
``````

but

``````julia> trunc(UInt16, prevfloat(typemax(Float16)))
0xffe0
``````

Also:

``````julia> trunc(UInt32, prevfloat(typemax(Float32)))
ERROR: InexactError: trunc(UInt32, 3.4028235e38)
Stacktrace:
 trunc(::Type{UInt32}, ::Float32) at ./float.jl:682
 top-level scope at REPL:1

julia> trunc(UInt64, prevfloat(typemax(Float64)))
ERROR: InexactError: trunc(UInt64, 1.7976931348623157e308)
Stacktrace:
 trunc(::Type{UInt64}, ::Float64) at ./float.jl:682
 top-level scope at REPL:1
``````

So I do not believe this holds for larger Floating Point numbers for which the exponent is bigger.

No, I meant Int16, but due to an incomplete understanding of `maxintfloat`, as pointed out by @stevengj .