diff --git a/base/range.jl b/base/range.jl index 3e1cd77eb914b..e9d28daf3ba3b 100644 --- a/base/range.jl +++ b/base/range.jl @@ -802,21 +802,15 @@ let bigints = Union{Int, UInt, Int64, UInt64, Int128, UInt128}, # slightly more accurate length and checked_length in extreme cases # (near typemax) for types with known `unsigned` functions function length(r::OrdinalRange{T}) where T<:bigints - @inline s = step(r) diff = last(r) - first(r) isempty(r) && return zero(diff) - # if |s| > 1, diff might have overflowed, but unsigned(diff)÷s should - # therefore still be valid (if the result is representable at all) - # n.b. !(s isa T) - if s isa Unsigned || -1 <= s <= 1 || s == -s - a = div(diff, s) % typeof(diff) - elseif s < 0 - a = div(unsigned(-diff), -s) % typeof(diff) - else - a = div(unsigned(diff), s) % typeof(diff) - end - return a + oneunit(a) + # Compute `(diff ÷ s) + 1` in a manner robust to signed overflow + # by using the absolute values as unsigneds for non-empty ranges. + # Note that `s` may be a different type from T and diff; it may not + # even be a BitInteger that supports `unsigned`. Handle with care. + a = div(unsigned(flipsign(diff, s)), s) % typeof(diff) + return flipsign(a, s) + oneunit(a) end function checked_length(r::OrdinalRange{T}) where T<:bigints s = step(r) diff --git a/test/ranges.jl b/test/ranges.jl index 40dbf6a42dbd9..a53bd4727f73c 100644 --- a/test/ranges.jl +++ b/test/ranges.jl @@ -743,6 +743,8 @@ end @test length(typemin(T):typemax(T)) == T(0) @test length(zero(T):one(T):typemax(T)) == typemin(T) @test length(typemin(T):one(T):typemax(T)) == T(0) + @test length(StepRange{T,BigInt}(zero(T), 1, typemax(T))) == typemin(T) + @test length(StepRange{T,BigInt}(typemin(T), 1, typemax(T))) == T(0) @test_throws OverflowError checked_length(zero(T):typemax(T)) @test_throws OverflowError checked_length(typemin(T):typemax(T)) @test_throws OverflowError checked_length(zero(T):one(T):typemax(T))