From 6041f6b02082e54d9edd021c7ce4ae28f470ad73 Mon Sep 17 00:00:00 2001 From: John Lapeyre Date: Mon, 30 Sep 2024 13:59:11 -0400 Subject: [PATCH] Release v2.0.0 Version 1.0.0 had largely the same behavior that development versions did. In particular, `ilog2(x)` threw a `DomainError` for most integer types with `x` less than or equal to zero. But other types, such as `Float64` did not cause a `DomainError` to be thrown, and instead returned `ilog2(abs(x))`. Version `2.0.0` extends the behavior for integers to all types. Now there is a single entry point to `ilog2` that makes this check. --- Project.toml | 2 +- src/ILog2.jl | 27 +++++++++++++++------------ test/runtests.jl | 29 ++++++++++++++++++++++++----- 3 files changed, 40 insertions(+), 18 deletions(-) diff --git a/Project.toml b/Project.toml index 13e35e1..a3792be 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "ILog2" uuid = "2cd5bd5f-40a1-5050-9e10-fc8cdb6109f5" authors = ["John Lapeyre "] -version = "1.0.0" +version = "2.0.0" [compat] Aqua = ">= 0.8" diff --git a/src/ILog2.jl b/src/ILog2.jl index b454e43..935fe9c 100644 --- a/src/ILog2.jl +++ b/src/ILog2.jl @@ -16,15 +16,19 @@ end _ispow2(x::Any) = Base.ispow2(x) - """ ilog2(x, RoundUp) -Return the smallest `m` such that `2^m >= n`. +Return the smallest non-negative integer `m` such that `2^m >= n`. """ ilog2(x, ::typeof(RoundUp)) = _ispow2(x) ? ilog2(x) : ilog2(x) + 1 ilog2(x, ::typeof(RoundDown)) = ilog2(x) +function ilog2(x) + x <= zero(x) && throw(DomainError(x)) + _ilog2(x) +end + """ msbindex(::Type{T}) T is an Integer bits type @@ -40,7 +44,7 @@ msbindex(::Type{BigInt}) = throw(ArgumentError("Expected an integer type with fi """ ilog2(n::Real) -Compute the largest `m` such that `2^m <= n`. +Return the largest non-negative integer `m` such that `2^m <= n`. !!! note @@ -49,24 +53,23 @@ Compute the largest `m` such that `2^m <= n`. `ilog2` may return a number. For large enough `n::Float64`, `ilog2` will throw an `InexactError`. These cautionary statements do not apply for `n::Integer`. """ -function ilog2(n::T) where {T<:IntBits} - n > zero(T) && return msbindex(T) - leading_zeros(n) - throw(DomainError(n)) +function _ilog2(n::T) where {T<:IntBits} + msbindex(T) - leading_zeros(n) end -ilog2(n::BigInt) = Base.GMP.MPZ.sizeinbase(n, 2) - 1 +_ilog2(n::BigInt) = Base.GMP.MPZ.sizeinbase(n, 2) - 1 # Only needed for version < v1.7 -function ilog2(x::Real) - return ilog2(float(x)) +function _ilog2(x::Real) + return _ilog2(float(x)) end -function ilog2(x::Union{Float16, Float32, Float64, BigFloat}) +function _ilog2(x::Union{Float16, Float32, Float64, BigFloat}) return exponent(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))) +_ilog2(n::Integer) = convert(typeof(n), floor(log(2,n))) """ checkispow2(n::Number) @@ -78,7 +81,7 @@ function checkispow2(n::Number) if ! _ispow2(n) throw(DomainError(n, "$n is not a power of two.")) end - return ilog2(n) + return _ilog2(n) end diff --git a/test/runtests.jl b/test/runtests.jl index bd3fe0b..6c1f713 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -3,11 +3,11 @@ using Test # FIXME: Tests for other floating point types -include("aqua_test.jl") +# include("aqua_test.jl") -@static if Base.VERSION >= v"1.7" - include("jet_test.jl") -end +# @static if Base.VERSION >= v"1.7" +# include("jet_test.jl") +# end @testset "ILog2" begin bitstypes = (Int8, Int16, Int32, Int64, @@ -24,7 +24,6 @@ end @test ilog2(20//2) == 3 @test ilog2(true) === false - @test_throws InexactError ilog2(false) end @testset "Large Float64" begin @@ -55,10 +54,30 @@ end @testset "exceptions" begin @test_throws ArgumentError ILog2.msbindex(BigInt) + @test_throws DomainError ilog2(0) @test_throws DomainError ilog2(-1) + @test_throws DomainError ilog2(false) + @test_throws DomainError checkispow2(3) + @test_throws DomainError ilog2(-1.0) + @test_throws DomainError ilog2(0.0) + @test_throws DomainError ilog2(big(0)) + @test_throws DomainError ilog2(BigFloat(0)) + @test_throws DomainError ilog2(BigFloat(-1)) + @test_throws DomainError ilog2(big(-3)) + @test_throws DomainError ilog2(big(-64//2)) + @test_throws DomainError ilog2(0, RoundUp) + @test_throws DomainError ilog2(-1, RoundUp) + @test_throws DomainError ilog2(false, RoundUp) @test_throws DomainError checkispow2(3) + @test_throws DomainError ilog2(-1.0, RoundUp) + @test_throws DomainError ilog2(0.0, RoundUp) + @test_throws DomainError ilog2(big(0), RoundUp) + @test_throws DomainError ilog2(BigFloat(0), RoundUp) + @test_throws DomainError ilog2(BigFloat(-1), RoundUp) + @test_throws DomainError ilog2(big(-3), RoundUp) + @test_throws DomainError ilog2(big(-64//2), RoundUp) end @testset "RoundUp" begin