From 8659cfeda7afa5cf665e282e927812f0a9d51d03 Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Mon, 21 Sep 2020 18:17:40 -0400 Subject: [PATCH] ispow2(x) for non-Integer x (#37635) --- NEWS.md | 1 + base/float.jl | 2 ++ base/intfuncs.jl | 18 ++++++++++++++++-- base/rational.jl | 1 + test/complex.jl | 7 +++++++ test/floatfuncs.jl | 11 +++++++++++ test/rational.jl | 7 +++++++ 7 files changed, 45 insertions(+), 2 deletions(-) diff --git a/NEWS.md b/NEWS.md index 15229f5c28f58..f507c77a65e65 100644 --- a/NEWS.md +++ b/NEWS.md @@ -91,6 +91,7 @@ Standard library changes by passing a tuple for `dims`, and defaults to reversing all dimensions; there is also a multidimensional in-place `reverse!(A; dims)` ([#37367]). * The function `isapprox(x,y)` now accepts the `norm` keyword argument also for numeric (i.e., non-array) arguments `x` and `y` ([#35883]). +* `ispow2(x)` now supports non-`Integer` arguments `x` ([#37635]). * `view`, `@view`, and `@views` now work on `AbstractString`s, returning a `SubString` when appropriate ([#35879]). * All `AbstractUnitRange{<:Integer}`s now work with `SubString`, `view`, `@view` and `@views` on strings ([#35879]). * `sum`, `prod`, `maximum`, and `minimum` now support `init` keyword argument ([#36188], [#35839]). diff --git a/base/float.jl b/base/float.jl index 0857d172eaff0..242fddfca8946 100644 --- a/base/float.jl +++ b/base/float.jl @@ -711,6 +711,8 @@ function issubnormal(x::T) where {T<:IEEEFloat} (y & exponent_mask(T) == 0) & (y & significand_mask(T) != 0) end +ispow2(x::AbstractFloat) = !iszero(x) && frexp(x)[1] == 0.5 + @eval begin typemin(::Type{Float16}) = $(bitcast(Float16, 0xfc00)) typemax(::Type{Float16}) = $(Inf16) diff --git a/base/intfuncs.jl b/base/intfuncs.jl index 198f73191ecb8..de790df019281 100644 --- a/base/intfuncs.jl +++ b/base/intfuncs.jl @@ -368,9 +368,9 @@ _prevpow2(x::Unsigned) = one(x) << unsigned((sizeof(x)<<3)-leading_zeros(x)-1) _prevpow2(x::Integer) = reinterpret(typeof(x),x < 0 ? -_prevpow2(unsigned(-x)) : _prevpow2(unsigned(x))) """ - ispow2(n::Integer) -> Bool + ispow2(n::Number) -> Bool -Test whether `n` is a power of two. +Test whether `n` is an integer power of two. # Examples ```jldoctest @@ -379,8 +379,22 @@ true julia> ispow2(5) false + +julia> ispow2(4.5) +false + +julia> ispow2(0.25) +true + +julia> ispow2(1//8) +true ``` + +!!! compat "Julia 1.6" + Support for non-`Integer` arguments was added in Julia 1.6. """ +ispow2(x::Number) = isreal(x) && ispow2(real(x)) + ispow2(x::Integer) = x > 0 && count_ones(x) == 1 """ diff --git a/base/rational.jl b/base/rational.jl index 1f7b0bea79ca4..e92c61c03a7ec 100644 --- a/base/rational.jl +++ b/base/rational.jl @@ -262,6 +262,7 @@ typemin(::Type{Rational{T}}) where {T<:Integer} = unsafe_rational(T, zero(T), on typemax(::Type{Rational{T}}) where {T<:Integer} = unsafe_rational(T, one(T), zero(T)) isinteger(x::Rational) = x.den == 1 +ispow2(x::Rational) = ispow2(x.num) & ispow2(x.den) +(x::Rational) = unsafe_rational(+x.num, x.den) -(x::Rational) = unsafe_rational(-x.num, x.den) diff --git a/test/complex.jl b/test/complex.jl index 07183d7fca354..f02c931ebdda7 100644 --- a/test/complex.jl +++ b/test/complex.jl @@ -1187,3 +1187,10 @@ end # complex with non-concrete eltype @test_throws ErrorException complex(Union{Complex{Int}, Nothing}[]) + +@testset "ispow2" begin + @test ispow2(4+0im) + @test ispow2(0.25+0im) + @test !ispow2(4+5im) + @test !ispow2(7+0im) +end diff --git a/test/floatfuncs.jl b/test/floatfuncs.jl index ff105f425ea81..677c07023c85d 100644 --- a/test/floatfuncs.jl +++ b/test/floatfuncs.jl @@ -40,6 +40,17 @@ end end end +@testset "ispow2" begin + for T in (Float16,Float32,Float64,BigFloat) + for x in (0.25, 1.0, 4.0, exp2(T(exponent(floatmax(T)))), exp2(T(exponent(floatmin(T))))) + @test ispow2(T(x)) + end + for x in (1.5, 0.0, 7.0, NaN, Inf) + @test !ispow2(T(x)) + end + end +end + @testset "round" begin for elty in (Float32, Float64) x = rand(elty) diff --git a/test/rational.jl b/test/rational.jl index 81a9ab16c5ff7..76ff20acbf938 100644 --- a/test/rational.jl +++ b/test/rational.jl @@ -594,3 +594,10 @@ end @test -1//5 * 0x3//0x2 == 0x3//0x2 * -1//5 == -3//10 @test -2//3 * 0x1 == 0x1 * -2//3 == -2//3 end + +@testset "ispow2" begin + @test ispow2(4//1) + @test ispow2(1//8) + @test !ispow2(3//8) + @test !ispow2(0//1) +end