From 1ce3d6c2c655aef9266db3fd32af98ee21e6e94a Mon Sep 17 00:00:00 2001 From: Neven Sajko Date: Sat, 6 May 2023 22:46:38 +0200 Subject: [PATCH] fix `nextpow`, `prevpow` for types without `typemax` Without this change `prevpow` and `nextpow` fail for, e.g., `BigInt`; incorrectly throwing a `MethodError`. For example, calls like `prevpow(3, big"10")` or `nextpow(3, big"10")` fail. The issue is that the code incorrectly assumes the existence of a `typemax` method. Another issue, revealed after fixing the previous one, was a missing `promote` for the arguments of a `mul_with_overflow` call. Fixes #57906 --- base/intfuncs.jl | 8 +++++--- test/intfuncs.jl | 17 ++++++++++++++++- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/base/intfuncs.jl b/base/intfuncs.jl index e14f48170f0e8..fccd8fcb9accc 100644 --- a/base/intfuncs.jl +++ b/base/intfuncs.jl @@ -570,7 +570,8 @@ function nextpow(a::Real, x::Real) n = ceil(Integer,log(a, x)) # round-off error of log can go either direction, so need some checks p = a^(n-1) - x > typemax(p) && throw(DomainError(x,"argument is beyond the range of type of the base")) + hastypemax(typeof(p)) && x > typemax(p) && + throw(DomainError(x,"argument is beyond the range of type of the base")) p >= x && return p wp = a^n wp > p || throw(OverflowError("result is beyond the range of type of the base")) @@ -611,9 +612,10 @@ function prevpow(a::T, x::Real) where T <: Real n = floor(Integer,log(a, x)) # round-off error of log can go either direction, so need some checks p = a^n - x > typemax(p) && throw(DomainError(x,"argument is beyond the range of type of the base")) + hastypemax(typeof(p)) && x > typemax(p) && + throw(DomainError(x,"argument is beyond the range of type of the base")) if a isa Integer - wp, overflow = mul_with_overflow(a, p) + wp, overflow = mul_with_overflow(promote(a, p)...) wp <= x && !overflow && return wp else wp = a^(n+1) diff --git a/test/intfuncs.jl b/test/intfuncs.jl index 38f29344d2f30..adcfee2a050dd 100644 --- a/test/intfuncs.jl +++ b/test/intfuncs.jl @@ -326,6 +326,14 @@ end end @testset "nextpow/prevpow" begin + fs = (prevpow, nextpow) + types = (Int8, BigInt, BigFloat) + for f ∈ fs, P ∈ types, R ∈ types, p ∈ 1:20, r ∈ 2:5 + q = P(p) + n = R(r) + @test f(r, p) == f(n, q) + end + @test nextpow(2, 3) == 4 @test nextpow(2, 4) == 4 @test nextpow(2, 7) == 8 @@ -339,7 +347,14 @@ end @test prevpow(10, 101.0) === 100 @test prevpow(10.0, 101) === 100.0 @test_throws DomainError prevpow(0, 3) - @test_throws DomainError prevpow(0, 3) + @test_throws DomainError prevpow(3, 0) + + # "argument is beyond the range of type of the base" + @test_throws DomainError prevpow(Int8(3), 243) + @test_throws DomainError nextpow(Int8(3), 243) + + # "result is beyond the range of type of the base" + @test_throws OverflowError nextpow(Int8(3), 82) end @testset "ndigits/ndigits0z" begin