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

Absolutely, positively perfect ranges. Never wrong. No way. #23194

Merged
merged 4 commits into from
Aug 22, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 13 additions & 31 deletions base/float.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# This file is a part of Julia. License is MIT: https://julialang.org/license

const IEEEFloat = Union{Float16, Float32, Float64}
Copy link
Contributor

Choose a reason for hiding this comment

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

I realise that it is beyond the scope of this PR, but it would be nice if this were an abstract type so I could reuse all this for Float128 once #22649 is fixed.

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 just moved this from Base.Math. I'll leave that kind of change to you 😄. Should be pretty simple, though.


## floating point traits ##

"""
Expand Down Expand Up @@ -605,7 +607,7 @@ uabs(x::Signed) = unsigned(abs(x))
The result of `n` iterative applications of `nextfloat` to `x` if `n >= 0`, or `-n`
applications of `prevfloat` if `n < 0`.
"""
function nextfloat(f::Union{Float16,Float32,Float64}, d::Integer)
function nextfloat(f::IEEEFloat, d::Integer)
F = typeof(f)
fumax = reinterpret(Unsigned, F(Inf))
U = typeof(fumax)
Expand Down Expand Up @@ -711,12 +713,12 @@ end

Test whether a floating point number is subnormal.
"""
function issubnormal end
function issubnormal(x::T) where {T<:IEEEFloat}
y = reinterpret(Unsigned, x)
(y & exponent_mask(T) == 0) & (y & significand_mask(T) != 0)
end

@eval begin
issubnormal(x::Float32) = (abs(x) < $(bitcast(Float32, 0x00800000))) & (x!=0)
issubnormal(x::Float64) = (abs(x) < $(bitcast(Float64, 0x0010000000000000))) & (x!=0)

typemin(::Type{Float16}) = $(bitcast(Float16, 0xfc00))
typemax(::Type{Float16}) = $(Inf16)
typemin(::Type{Float32}) = $(-Inf32)
Expand Down Expand Up @@ -864,32 +866,11 @@ exponent_half(::Type{Float16}) = 0x3800
significand_mask(::Type{Float16}) = 0x03ff

# integer size of float
fpinttype(::Type{Float64}) = UInt64
fpinttype(::Type{Float32}) = UInt32
fpinttype(::Type{Float16}) = UInt16

## TwicePrecision utilities
# The numeric constants are half the number of bits in the mantissa
for (F, T, n) in ((Float16, UInt16, 5), (Float32, UInt32, 12), (Float64, UInt64, 26))
@eval begin
function truncbits(x::$F, nb)
@_inline_meta
truncmask(x, typemax($T) << nb)
end
function truncmask(x::$F, mask)
@_inline_meta
reinterpret($F, mask & reinterpret($T, x))
end
function splitprec(x::$F)
@_inline_meta
hi = truncmask(x, typemax($T) << $n)
hi, x-hi
end
end
end
uinttype(::Type{Float64}) = UInt64
uinttype(::Type{Float32}) = UInt32
uinttype(::Type{Float16}) = UInt16

truncbits(x, nb) = x
truncmask(x, mask) = x
Base.iszero(x::Float16) = reinterpret(UInt16, x) & ~sign_mask(Float16) == 0x0000

## Array operations on floating point numbers ##

Expand All @@ -904,7 +885,8 @@ end

float(r::StepRange) = float(r.start):float(r.step):float(last(r))
float(r::UnitRange) = float(r.start):float(last(r))
float(r::StepRangeLen) = StepRangeLen(float(r.ref), float(r.step), length(r), r.offset)
float(r::StepRangeLen{T}) where {T} =
StepRangeLen{typeof(float(T(r.ref)))}(float(r.ref), float(r.step), length(r), r.offset)
function float(r::LinSpace)
LinSpace(float(r.start), float(r.stop), length(r))
end
Expand Down
8 changes: 4 additions & 4 deletions base/math.jl
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ import Base: log, exp, sin, cos, tan, sinh, cosh, tanh, asin,
exp10, expm1, log1p

using Base: sign_mask, exponent_mask, exponent_one,
exponent_half, fpinttype, significand_mask
exponent_half, uinttype, significand_mask

using Core.Intrinsics: sqrt_llvm

const IEEEFloat = Union{Float16, Float32, Float64}
using Base.IEEEFloat

@noinline function throw_complex_domainerror(f, x)
throw(DomainError(x, string("$f will only return a complex result if called with a ",
Expand Down Expand Up @@ -588,7 +588,7 @@ function ldexp(x::T, e::Integer) where T<:IEEEFloat
return flipsign(T(Inf), x)
end
if k > 0 # normal case
xu = (xu & ~exponent_mask(T)) | (rem(k, fpinttype(T)) << significand_bits(T))
xu = (xu & ~exponent_mask(T)) | (rem(k, uinttype(T)) << significand_bits(T))
return reinterpret(T, xu)
else # subnormal case
if k <= -significand_bits(T) # underflow
Expand All @@ -598,7 +598,7 @@ function ldexp(x::T, e::Integer) where T<:IEEEFloat
end
k += significand_bits(T)
z = T(2.0)^-significand_bits(T)
xu = (xu & ~exponent_mask(T)) | (rem(k, fpinttype(T)) << significand_bits(T))
xu = (xu & ~exponent_mask(T)) | (rem(k, uinttype(T)) << significand_bits(T))
return z*reinterpret(T, xu)
end
end
Expand Down
46 changes: 31 additions & 15 deletions base/range.jl
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,8 @@ end

StepRangeLen(ref::R, step::S, len::Integer, offset::Integer = 1) where {R,S} =
StepRangeLen{typeof(ref+0*step),R,S}(ref, step, len, offset)
StepRangeLen{T}(ref::R, step::S, len::Integer, offset::Integer = 1) where {T,R,S} =
StepRangeLen{T,R,S}(ref, step, len, offset)

## linspace and logspace

Expand Down Expand Up @@ -370,9 +372,12 @@ julia> step(linspace(2.5,10.9,85))
"""
step(r::StepRange) = r.step
step(r::AbstractUnitRange) = 1
step(r::StepRangeLen) = r.step
step(r::StepRangeLen{T}) where {T} = T(r.step)
step(r::LinSpace) = (last(r)-first(r))/r.lendiv

step_hp(r::StepRangeLen) = r.step
step_hp(r::Range) = step(r)

unsafe_length(r::Range) = length(r) # generic fallback

function unsafe_length(r::StepRange)
Expand Down Expand Up @@ -455,10 +460,9 @@ done(r::StepRange, i) = isempty(r) | (i < min(r.start, r.stop)) | (i > max(r.sta
done(r::StepRange, i::Integer) =
isempty(r) | (i == oftype(i, r.stop) + r.step)

# see also twiceprecision.jl
start(r::StepRangeLen) = (unsafe_getindex(r, 1), 1)
next(r::StepRangeLen{T}, s) where {T} = s[1], (T(s[1]+r.step), s[2]+1)
done(r::StepRangeLen, s) = s[2] > length(r)
start(r::StepRangeLen) = 1
next(r::StepRangeLen{T}, i) where {T} = unsafe_getindex(r, i), i+1
done(r::StepRangeLen, i) = i > length(r)

start(r::UnitRange{T}) where {T} = oftype(r.start + oneunit(T), r.start)
next(r::AbstractUnitRange{T}, i) where {T} = (convert(T, i), i + oneunit(T))
Expand Down Expand Up @@ -496,7 +500,7 @@ end

function getindex(v::Range{T}, i::Integer) where T
@_inline_meta
ret = convert(T, first(v) + (i - 1)*step(v))
ret = convert(T, first(v) + (i - 1)*step_hp(v))
ok = ifelse(step(v) > zero(step(v)),
(ret <= v.stop) & (ret >= v.start),
(ret <= v.start) & (ret >= v.stop))
Expand All @@ -516,6 +520,11 @@ function unsafe_getindex(r::StepRangeLen{T}, i::Integer) where T
T(r.ref + u*r.step)
end

function _getindex_hiprec(r::StepRangeLen, i::Integer) # without rounding by T
u = i - r.offset
r.ref + u*r.step
end

function unsafe_getindex(r::LinSpace, i::Integer)
lerpi.(i-1, r.lendiv, r.start, r.stop)
end
Expand Down Expand Up @@ -556,11 +565,14 @@ function getindex(r::StepRange, s::Range{<:Integer})
range(st, step(r)*step(s), length(s))
end

function getindex(r::StepRangeLen, s::OrdinalRange{<:Integer})
function getindex(r::StepRangeLen{T}, s::OrdinalRange{<:Integer}) where {T}
@_inline_meta
@boundscheck checkbounds(r, s)
vfirst = unsafe_getindex(r, first(s))
return StepRangeLen(vfirst, r.step*step(s), length(s))
# Find closest approach to offset by s
ind = linearindices(s)
offset = max(min(1 + round(Int, (r.offset - first(s))/step(s)), last(ind)), first(ind))
ref = _getindex_hiprec(r, first(s) + (offset-1)*step(s))
return StepRangeLen{T}(ref, r.step*step(s), length(s), offset)
end

function getindex(r::LinSpace, s::OrdinalRange{<:Integer})
Expand Down Expand Up @@ -725,16 +737,17 @@ end
## linear operations on ranges ##

-(r::OrdinalRange) = range(-first(r), -step(r), length(r))
-(r::StepRangeLen) = StepRangeLen(-r.ref, -r.step, length(r), r.offset)
-(r::StepRangeLen{T,R,S}) where {T,R,S} =
StepRangeLen{T,R,S}(-r.ref, -r.step, length(r), r.offset)
-(r::LinSpace) = LinSpace(-r.start, -r.stop, length(r))

+(x::Real, r::AbstractUnitRange) = range(x + first(r), length(r))
# For #18336 we need to prevent promotion of the step type:
+(x::Number, r::AbstractUnitRange) = range(x + first(r), step(r), length(r))
+(x::Number, r::Range) = (x+first(r)):step(r):(x+last(r))
function +(x::Number, r::StepRangeLen)
function +(x::Number, r::StepRangeLen{T}) where T
newref = x + r.ref
StepRangeLen{eltype(newref),typeof(newref),typeof(r.step)}(newref, r.step, length(r), r.offset)
StepRangeLen{typeof(T(r.ref) + x)}(newref, r.step, length(r), r.offset)
end
function +(x::Number, r::LinSpace)
LinSpace(x + r.start, x + r.stop, r.len)
Expand All @@ -750,15 +763,18 @@ end
-(r::Range, x::Number) = +(-x, r)

*(x::Number, r::Range) = range(x*first(r), x*step(r), length(r))
*(x::Number, r::StepRangeLen) = StepRangeLen(x*r.ref, x*r.step, length(r), r.offset)
*(x::Number, r::StepRangeLen{T}) where {T} =
StepRangeLen{typeof(x*T(r.ref))}(x*r.ref, x*r.step, length(r), r.offset)
*(x::Number, r::LinSpace) = LinSpace(x * r.start, x * r.stop, r.len)
# separate in case of noncommutative multiplication
*(r::Range, x::Number) = range(first(r)*x, step(r)*x, length(r))
*(r::StepRangeLen, x::Number) = StepRangeLen(r.ref*x, r.step*x, length(r), r.offset)
*(r::StepRangeLen{T}, x::Number) where {T} =
StepRangeLen{typeof(T(r.ref)*x)}(r.ref*x, r.step*x, length(r), r.offset)
*(r::LinSpace, x::Number) = LinSpace(r.start * x, r.stop * x, r.len)

/(r::Range, x::Number) = range(first(r)/x, step(r)/x, length(r))
/(r::StepRangeLen, x::Number) = StepRangeLen(r.ref/x, r.step/x, length(r), r.offset)
/(r::StepRangeLen{T}, x::Number) where {T} =
StepRangeLen{typeof(T(r.ref)/x)}(r.ref/x, r.step/x, length(r), r.offset)
/(r::LinSpace, x::Number) = LinSpace(r.start / x, r.stop / x, r.len)

/(x::Number, r::Range) = [ x/y for y=r ]
Expand Down
4 changes: 2 additions & 2 deletions base/special/exp.jl
Original file line number Diff line number Diff line change
Expand Up @@ -117,11 +117,11 @@ function exp(x::T) where T<:Union{Float32,Float64}
if k > -significand_bits(T)
# multiply by 2.0 first to prevent overflow, which helps extends the range
k == exponent_max(T) && return y * T(2.0) * T(2.0)^(exponent_max(T) - 1)
twopk = reinterpret(T, rem(exponent_bias(T) + k, fpinttype(T)) << significand_bits(T))
twopk = reinterpret(T, rem(exponent_bias(T) + k, uinttype(T)) << significand_bits(T))
return y*twopk
else
# add significand_bits(T) + 1 to lift the range outside the subnormals
twopk = reinterpret(T, rem(exponent_bias(T) + significand_bits(T) + 1 + k, fpinttype(T)) << significand_bits(T))
twopk = reinterpret(T, rem(exponent_bias(T) + significand_bits(T) + 1 + k, uinttype(T)) << significand_bits(T))
return y * twopk * T(2.0)^(-significand_bits(T) - 1)
end
elseif xa < reinterpret(Unsigned, exp_small_thres(T)) # |x| < exp_small_thres
Expand Down
4 changes: 2 additions & 2 deletions base/special/exp10.jl
Original file line number Diff line number Diff line change
Expand Up @@ -119,11 +119,11 @@ function exp10(x::T) where T<:Union{Float32,Float64}
if k > -significand_bits(T)
# multiply by 2.0 first to prevent overflow, extending the range
k == exponent_max(T) && return y * T(2.0) * T(2.0)^(exponent_max(T) - 1)
twopk = reinterpret(T, rem(exponent_bias(T) + k, fpinttype(T)) << significand_bits(T))
twopk = reinterpret(T, rem(exponent_bias(T) + k, uinttype(T)) << significand_bits(T))
return y*twopk
else
# add significand_bits(T) + 1 to lift the range outside the subnormals
twopk = reinterpret(T, rem(exponent_bias(T) + significand_bits(T) + 1 + k, fpinttype(T)) << significand_bits(T))
twopk = reinterpret(T, rem(exponent_bias(T) + significand_bits(T) + 1 + k, uinttype(T)) << significand_bits(T))
return y * twopk * T(2.0)^(-significand_bits(T) - 1)
end
elseif xa < reinterpret(Unsigned, exp10_small_thres(T)) # |x| < exp10_small_thres
Expand Down
2 changes: 1 addition & 1 deletion base/sysimg.jl
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,6 @@ include("tuple.jl")
include("pair.jl")
include("traits.jl")
include("range.jl")
include("twiceprecision.jl")
include("expr.jl")
include("error.jl")

Expand Down Expand Up @@ -135,6 +134,7 @@ include("hashing.jl")
include("rounding.jl")
importall .Rounding
include("float.jl")
include("twiceprecision.jl")
include("complex.jl")
include("rational.jl")
include("multinverses.jl")
Expand Down
Loading