Skip to content

Commit

Permalink
Merge pull request #9 from jlapeyre/use-exponent-for-abstract-float
Browse files Browse the repository at this point in the history
Simplify and improve ilog2(::Real)
  • Loading branch information
jlapeyre authored Aug 19, 2024
2 parents acffed1 + f952293 commit decc207
Show file tree
Hide file tree
Showing 2 changed files with 18 additions and 34 deletions.
46 changes: 12 additions & 34 deletions src/ILog2.jl
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,19 @@ export ilog2, checkispow2
const IntBits = Union{Int8, Int16, Int32, Int64, Int128,
UInt8, UInt16, UInt32, UInt64, UInt128}

@static if VERSION < v"1.6"
_ispow2(x::AbstractFloat) = !iszero(x) && frexp(x)[1] == 0.5
end

_ispow2(x::Any) = Base.ispow2(x)


"""
ilog2(x, RoundUp)
Return the smallest `m` such that `2^m >= n`.
"""
ilog2(x, ::typeof(RoundUp)) = ispow2(x) ? ilog2(x) : ilog2(x) + 1
ilog2(x, ::typeof(RoundUp)) = _ispow2(x) ? ilog2(x) : ilog2(x) + 1
ilog2(x, ::typeof(RoundDown)) = ilog2(x)

"""
Expand Down Expand Up @@ -49,43 +55,15 @@ function ilog2(n::T) where {T<:IntBits}
end
ilog2(n::BigInt) = Base.GMP.MPZ.sizeinbase(n, 2) - 1

# Only needed for version < v1.7
function ilog2(x::Real)
if x < typemax(Int)
return fastilog2(x)
end
return slowilog2(x)
return ilog2(float(x))
end

"""
slowilog2(x::Real)
This is slower than `ilog2` for `x::Float64` with `x` less than
about `1e19` (when `Int` is `Int64`); more precisely less than `typemax(Int64)`.
However, it is slightly faster than `ilog2` when `x > typemax(Int)`.
Use `slowilog2(x::Float64)` only under exceptional circumstances: when you need 10-20% faster
ilog2 for `Float64` and `x` is very large, ie `x > typemax(Int)`.
"""
@inline function slowilog2(x::Real)
return floor(Int, log2(x))
function ilog2(x::Union{Float16, Float32, Float64, BigFloat})
return exponent(x)
end

"""
fastilog2(x::AbstractFloat)
This is slightly faster than `ilog2` for `x::Float64` with `x` less than
about `1e19` (when `Int` is `Int64`); more precisely less than `typemax(Int64)`.
However, `fastilog2` will throw an `InexactError` for
when `x > typemax(Int)`.
Use `fastilog2(::Float64)` only under exceptional circumstances: when you need 10-20% faster
ilog2 for `Float64` and the input is small enough.
"""
@inline function fastilog2(x::AbstractFloat)
return ilog2(floor(Integer, x))
end


# This is several times slower than the other methods. But none of the standard bitstype integers,
# nor `BigInt`, dispatch to this method.
ilog2(n::Integer) = convert(typeof(n), floor(log(2,n)))
Expand All @@ -97,7 +75,7 @@ Return base-2 logarithm of `n` if `n` is a power of two.
Otherwise throw a `DomainError`.
"""
function checkispow2(n::Number)
if ! ispow2(n)
if ! _ispow2(n)
throw(DomainError(n, "$n is not a power of two."))
end
return ilog2(n)
Expand Down
6 changes: 6 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,10 @@ end
@test ilog2(15) == 3
@test ilog2(15, RoundDown) == 3
@test ilog2(15, RoundUp) == 4
for T in (Float16, Float32, Float64)
x = T(15.1)
@test ilog2(x) == 3
@test ilog2(x, RoundDown) == 3
@test ilog2(x, RoundUp) == 4
end
end

0 comments on commit decc207

Please sign in to comment.