Skip to content

Commit bb3be0d

Browse files
authored
fix powermod correctness on unsigned power of half typemax (#59680)
the comment claims: > When the concrete type of p is signed and has the lowest value, > `p != 0 && p == -p` is equivalent to `p == typemin(typeof(p))` this is true, but fails to consider that `p == -p` is also true when `p = div(typemax(UInt), 2) + 1`, and that `p` is not necessarily signed leading to incorrect results on inputs like ``` julia> powermod(0x03, 0x80, 0x07) 0x01 ``` which should be `0x02`
1 parent 694902b commit bb3be0d

File tree

2 files changed

+15
-10
lines changed

2 files changed

+15
-10
lines changed

base/intfuncs.jl

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -489,18 +489,20 @@ julia> powermod(5, 3, 19)
489489
function powermod(x::Integer, p::Integer, m::T) where T<:Integer
490490
p == 0 && return mod(one(m),m)
491491
# When the concrete type of p is signed and has the lowest value,
492-
# `p != 0 && p == -p` is equivalent to `p == typemin(typeof(p))` for 2's complement representation.
492+
# `p < 0 && p == -p` is equivalent to `p == typemin(typeof(p))` for 2's complement representation.
493493
# but will work for integer types like `BigInt` that don't have `typemin` defined
494494
# It needs special handling otherwise will cause overflow problem.
495-
if p == -p
496-
imod = invmod(x, m)
497-
rhalf = powermod(imod, -(p÷2), m)
498-
r::T = mod(widemul(rhalf, rhalf), m)
499-
isodd(p) && (r = mod(widemul(r, imod), m))
500-
#else odd
501-
return r
502-
elseif p < 0
503-
return powermod(invmod(x, m), -p, m)
495+
if p < 0
496+
if p == -p
497+
imod = invmod(x, m)
498+
rhalf = powermod(imod, -(p÷2), m)
499+
r::T = mod(widemul(rhalf, rhalf), m)
500+
isodd(p) && (r = mod(widemul(r, imod), m))
501+
#else odd
502+
return r
503+
else
504+
return powermod(invmod(x, m), -p, m)
505+
end
504506
end
505507
(m == 1 || m == -1) && return zero(m)
506508
b = oftype(m,mod(x,m)) # this also checks for divide by zero

test/intfuncs.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,9 @@ end
359359
@test powermod(2, big(3), -5) == -2
360360
@inferred powermod(2, -2, -5)
361361
@inferred powermod(big(2), -2, UInt(5))
362+
363+
@test powermod(-3, 0x80, 7) === 2
364+
@test powermod(0x03, 0x80, 0x07) === 0x02
362365
end
363366

364367
@testset "nextpow/prevpow" begin

0 commit comments

Comments
 (0)