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

Add signed(T), unsigned(T). #30445

Closed
wants to merge 12 commits into from
2 changes: 2 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ New language features
* `import` now allows quoted symbols, e.g. `import Base.:+` ([#33158]).
* `a[begin]` can now be used to address the first element of an integer-indexed collection `a`.
The index is computed by `firstindex(a)` ([#33946]).
* There are new method overloads `unsigned(T::Type)` and `signed(T::Type)` that accept a `Type`,
and return its corresponding signed/unsigned equivalent type ([#30445]).

Language changes
----------------
Expand Down
35 changes: 35 additions & 0 deletions base/int.jl
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,41 @@ signed without checking for overflow.
signed(x) = convert(Signed, x)
signed(x::BitUnsigned) = reinterpret(typeof(convert(Signed, zero(x))), x)


"""
unsigned(T::Type)

Return the corresponding unsigned type for type `T`.

!!! compat "Julia 1.4"
signed(T)/unsigned(T) on Types requires Julia 1.4 or later.

NHDaly marked this conversation as resolved.
Show resolved Hide resolved
# Examples
```jldoctest
julia> unsigned(Int8)
UInt8
```
"""
unsigned(::Type{T}) where {T<:Unsigned} = T
unsigned(::Type{T}) where {T<:BitSigned} = typeof(unsigned(zero(T)))
"""
signed(T::Type)

Return the corresponding signed type for type `T`.

!!! compat "Julia 1.4"
signed(T)/unsigned(T) on Types requires Julia 1.4 or later.

#Examples
```jldoctest
julia> signed(UInt8)
Int8
```
"""
NHDaly marked this conversation as resolved.
Show resolved Hide resolved
signed(::Type{T}) where {T<:Signed} = T
signed(::Type{T}) where {T<:BitUnsigned} = typeof(signed(zero(T)))


div(x::BitSigned, y::Unsigned) = flipsign(signed(div(unsigned(abs(x)), y)), x)
div(x::Unsigned, y::BitSigned) = unsigned(flipsign(signed(div(x, unsigned(abs(y)))), y))

Expand Down
11 changes: 2 additions & 9 deletions base/multinverses.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,10 @@

module MultiplicativeInverses

import Base: div, divrem, rem, unsigned
using Base: IndexLinear, IndexCartesian, tail
import Base: div, divrem, rem
using Base: IndexLinear, IndexCartesian, tail, unsigned
export multiplicativeinverse

unsigned(::Type{Int8}) = UInt8
unsigned(::Type{Int16}) = UInt16
unsigned(::Type{Int32}) = UInt32
unsigned(::Type{Int64}) = UInt64
unsigned(::Type{Int128}) = UInt128
unsigned(::Type{T}) where {T<:Unsigned} = T

abstract type MultiplicativeInverse{T} end

# Computes integer division by a constant using multiply, add, and bitshift.
Expand Down
27 changes: 22 additions & 5 deletions test/int.jl
Original file line number Diff line number Diff line change
Expand Up @@ -54,16 +54,33 @@ using Random
end
end
end
@testset "signed and unsigned" begin
@test signed(3) == 3
@test signed(UInt(3)) == 3
@testset "signed" begin
@test signed(3) === 3
@test signed(UInt(3)) === 3
@test isa(signed(UInt(3)), Int)
@test signed(UInt(0) - 1) == -1
@test_throws InexactError signed(UInt(-3))
@test signed(UInt(0) - 1) === -1
@test_throws InexactError signed(UInt(-3)) # Note that UInt() throws, not signed().
@test signed(true) == 1
end
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed all these tests to be === from ==, because they would be equal even if signed() did nothing. Lemme know if you don't like that! :)

Also, I was a bit confused by the @test_throws here. I clarified its behavior with this # Note, but maybe it's better to fix/clarify the test itself? I'm not reallly sure what it's testing though, unfortunately.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand that test either. Does git blame shed any light?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like the test was added here: #11962, during v0.4.0.

Maybe the behavior was different back then, or maybe it was a mistake? (It was David's first PR ever after all! 😋) I'm happy to either remove the whole line or just leave the comment; whatever you prefer!

@testset "unsigned" begin
@test unsigned(3) === UInt(3)
@test unsigned(UInt(3)) === UInt(3)
@test isa(unsigned(3), UInt)
@test unsigned(-1) === typemax(UInt)
@test_throws InexactError unsigned(Int(typemax(UInt))) # Int(0xffffffff...) throws
@test unsigned(true) isa Unsigned
@test unsigned(true) == unsigned(1)
end
@testset "[un]signed(::Type{T})" begin
@testset for (T,UT) in ((Int8,UInt8), (Int,UInt), (Int128,UInt128))
@test unsigned(T) === UT
@test unsigned(UT) === UT
@test signed(T) === T
@test signed(UT) === T
end
@test signed(typeof(0x3))(0x3) === signed(0x3)
@test unsigned(typeof(3))(3) === unsigned(3)
end
@testset "bswap" begin
@test bswap(Int8(3)) == 3
@test bswap(UInt8(3)) == 3
Expand Down