Skip to content
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
18 changes: 16 additions & 2 deletions src/kernels.jl
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,20 @@ end
# BinaryBracket.
# ===========================================================================

# Truncate a guess coordinate `f` to an `Int` offset. The caller has already
# clamped `f` to `[0, nm1)` (last) / `(0, nm1]` (first), so on the hardware
# floats the fast `unsafe_trunc` is in-range and skips a redundant check.
# Other ordered-`Real` ratio types — `Rational`, AD `Dual`, `BigFloat`, … —
# do not all define `unsafe_trunc`, so fall back to the checked
# `floor(Int, ·)` / `ceil(Int, ·)`, which every such type supports and which
# is exact for the in-range `f`. The hardware-float union is spelled out
# rather than via the non-public `Base.IEEEFloat` alias.
const _HardwareFloat = Union{Float16, Float32, Float64}
@inline _uniform_floor_index(f::_HardwareFloat) = unsafe_trunc(Int, floor(f))
@inline _uniform_floor_index(f) = floor(Int, f)
@inline _uniform_ceil_index(f::_HardwareFloat) = unsafe_trunc(Int, ceil(f))
@inline _uniform_ceil_index(f) = ceil(Int, f)

@inline function _kernel_last_uniform_step_props(
props::SearchProperties, v::AbstractVector, x, order::Base.Order.Ordering,
)
Expand All @@ -605,7 +619,7 @@ end
elseif f >= nm1
lastindex(v)
else
firstindex(v) + unsafe_trunc(Int, floor(f))
firstindex(v) + _uniform_floor_index(f)
end
# Walk to the true cell. For exactly-uniform data this takes at most
# one step (float roundoff); it also keeps the result correct when
Expand Down Expand Up @@ -640,7 +654,7 @@ end
elseif f > nm1
lastindex(v) + 1
else
firstindex(v) + unsafe_trunc(Int, ceil(f))
firstindex(v) + _uniform_ceil_index(f)
end
@inbounds while i > firstindex(v) && !Base.Order.lt(order, v[i - 1], x)
i -= 1
Expand Down
30 changes: 30 additions & 0 deletions test/core_tests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1398,3 +1398,33 @@ end
@test F.findfirstsortedequal(x, v) == ref
end
end

@safetestset "UniformStep kernel across non-primitive Real eltypes" begin
using FindFirstFunctions
using FindFirstFunctions: SearchProperties, Auto, searchsorted_last, searchsorted_first

# `Rational` ratio types don't define `unsafe_trunc(Int, ·)`, so a uniform
# `Rational` vector — which `Auto` resolves to the closed-form
# `KIND_UNIFORM_STEP` path — must fall back to `floor/ceil(Int, ·)` rather
# than erroring. `BigFloat` exercises the non-`IEEEFloat` AbstractFloat
# branch of the same helper.
uniform_vectors = (
Rational{Int}[i // 4 for i in 0:4:160],
Rational{BigInt}[big(i) // 4 for i in 0:4:160],
BigFloat[BigFloat(i) for i in range(0.0, 10.0; length = 40)],
)
for v in uniform_vectors
@test SearchProperties(v).is_uniform
auto = Auto(v)
@test auto isa Auto
for i in eachindex(v)
@test searchsorted_last(auto, v, v[i]) == searchsortedlast(v, v[i])
@test searchsorted_first(auto, v, v[i]) == searchsortedfirst(v, v[i])
end
# Between-knot and out-of-range queries.
for q in (v[1] - one(eltype(v)), (v[3] + v[4]) / 2, v[end] + one(eltype(v)))
@test searchsorted_last(auto, v, q) == searchsortedlast(v, q)
@test searchsorted_first(auto, v, q) == searchsortedfirst(v, q)
end
end
end
Loading