-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
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
x % Unsigned, x % Signed #34864
x % Unsigned, x % Signed #34864
Conversation
This follows from a discussion on Slack; the implementation was suggested by @Keno.
base/int.jl
Outdated
@@ -506,6 +506,7 @@ if nameof(@__MODULE__) === :Base | |||
end | |||
|
|||
rem(x::T, ::Type{T}) where {T<:Integer} = x | |||
rem(x::Signed, ::Type{Unsigned}) = x % unsigned(typeof(x)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should probably also have the opposite x % Signed
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done and tests are also done
Covered News.md, src/int.jl, test/int.jl for both |
x::Signed % Unsigned
x::Signed % Unsigned
test/int.jl
Outdated
@@ -266,6 +266,14 @@ end | |||
U in [Base.BitInteger_types..., BigInt] | |||
@test typeof(rand(U(0):U(127)) % T) === T | |||
end | |||
@testset "x::Signed % Unsigned returns a T, T = $T" for T in [Base.BitSigned_types...] | |||
@test typeof(rand(T) % Unsigned) <: Unsigned |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rather this test a few interesting cases instead (or in addition). For example typemax
, typemin
, -typemax
. Also should test for x
a BigInt, since that's an interesting one for which there is no unsigned type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you think? Should BigInt(x) % Unsigned
always throw an error, or should it handle values that only require 128 signed bits and throw and error for larger values?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was surprised that unsigned(Int64) == UInt64
but signed(UInt64)
errors. We should remedy that, defining signed(x::T) where {T<:Base.BitUnsigned_types}
to be the size corresponding BitSigned_types
. I am making that change in int.jl as these BitInteger pairs should be with the BitInteger, BitSigned, BitUnsigned type declarations.
Half of them, the unsigned(T) where {T<:BitSigned}
are given in multinverse.jl, which is its own module. I am removing those lines from that file and adding unsigned
to the short list Base sourced things given with using Base:
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why support x::BigInt % Unsigned
? It doesn't really make sense, because BigInt
is not a bitstype and these conversions are from one n-byte integer type (signed or unsigned) to another n-byte integer type (unsigned or signed).
The error that happens .. no method matching Unsigned(::BigInt)
seems appropriate,
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed-size integer types represent modular classes of integer values. When you do x % T
you are saying "give me the element of that represents the modular class of x
in T
. That is a perfectly well-defined operation for x::BigInt % Unsigned
. In fact, this operation is always well-defined because x
represents a specific integer value which must belongs to exactly one modular class in T
and the result is the unique value in T
representing that class.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But in x::BigInt % Unsigned
, how to choose the destination type? We won't define an UnsignedBigInt
type. And we can't return x
because it's expected that (x % Unsigned) isa Unsigned
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I see what you mean—I interpreted that as being for some specific T <: Unsigned
but the issue is for the literal type Unsigned
. That is quite unclear so agree about it being an error. The alternative would be to choose UInt
as the "default unsigned type" but that's questionable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The alternative would be to choose UInt as the "default unsigned type" but that's questionable.
I guess it's always possible to add it later if there is demand for that, but while this makes some sense, this seems a bit weird to return a smaller type than Int128(1) % Unsigned
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree. Which leads to an issue if/when we ever add (U)Int256
types.
With Base and this PR with or without (U)Int256 bitstypes:
A package exporting a new pair of Signed and Unsigned integer types (
(this works for bitstypes, [mutable] structs, and external library types) |
I think it's worth discussing this on triage, particularly for the BigInt case. |
base/int.jl
Outdated
@@ -44,6 +44,22 @@ const BitUnsigned64T = Union{Type{UInt8}, Type{UInt16}, Type{UInt32}, Type{UIn | |||
|
|||
const BitIntegerType = Union{map(T->Type{T}, BitInteger_types)...} | |||
|
|||
# interconvert equilength BitInteger types | |||
|
|||
unsigned(::Type{Int8}) = UInt8 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These should already be defined. Why is this necessary?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unsigned(::Type{IntN}) = UIntN
are defined in Base/multinverses.jl
.
signed(::Type{UIntN}) = IntN
are not defined.
I thought the two sets of definitions belonged in the same place, and that Int.jl
was more appropriate than multinverses.jl
. I moved the defs from multinverses.jl
to Int.jl
and added the defs for signed(_)
there. I added unsigned
to the using Base:
line in multinverses.jl
.
I can revert this and I could add the signed(_)
defs into multinverses.jl
. multiinverses
is a module, would I need to export signed
after adding it there?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
all that is undone
Co-Authored-By: Matt Bauman <mbauman@gmail.com>
😄 |
error is unrelated
|
What's the status of this? Done? Waiting for CI to pass? |
This is done and it is good to go now |
NEWS.md
Outdated
@@ -92,6 +92,9 @@ New library features | |||
|
|||
* `isapprox` (or `≈`) now has a one-argument "curried" method `isapprox(x)` which returns a function, like `isequal` (or `==`)` ([#32305]). | |||
* `Ref{NTuple{N,T}}` can be passed to `Ptr{T}`/`Ref{T}` `ccall` signatures ([#34199]) | |||
* `accumulate`, `cumsum`, and `cumprod` now support `Tuple` ([#34654]). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Delete this line, which must come from rebasing (the correct version is indicated 3 lines below)
test/int.jl
Outdated
@test unsigned(S) === U | ||
end | ||
end | ||
@testset "x::[Un]Signed % [Un]Signed returns x" begin |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just personal taste, but I would join the three @testsets
together, as they contain the same loop, so it's a bit of a ceremony overhead.
This might need updating the docstrings (I know that |
arrgh -- ok about the doc strings -- |
I added the docstring for |
@StefanKarpinski there it be ✅ .. please merge before something else changes :)) |
This follows from a discussion on Slack (exerpted below); the implementation is from @Keno.
Matt Bauman: I guess I’m in a gripey mood today. Is there a reason why we don’t allow -1 % Unsigned?
Matt Bauman: Is the “correct” way to do this
unsigned(-1)
?Keno Fischer: yes, but I think
% Unsigned
is reasonable....
Kristoffer Carlsson: Why [is reinterpret] yuk? Isn't that the most descriptive function name to use? Who knows what semantics unsigned has?
Matt Bauman: For some reason, reinterpret feels like it imposes a higher cognitive burden — like I need to think about the exact bit representation. All I care about in this operation is ensuring that negative values become absurdly large. But it’s a moot point.
Keno Fischer: In this case rem is just using the convert fallback. I think a definition like
would be reasonable ... if somebody wants to submit a PR ... https://github.com/JuliaLang/julia/blob/master/base/int.jl#L508-L510
Stefan Karpinski: @KristofferC: the reason I don’t like reinterpret is that it forces the type of the input and the result to have the same size but this is a perfectly well-defined operation even for different sizes.