mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-15 01:13:30 +00:00
Fix integer.mul_by_digit producing denormalized zeros. (#141)
mul_by_digit(0, Y) produced i(Len, [0, 0, ...]) instead of the canonical integer.zero = i(0, []). Since is_zero/1 only matched the canonical form, denormalized zeros were silently treated as nonzero, causing incorrect results in rational.m (e.g., gcd_2 non-termination, division-by-zero crashes in big_quot_rem). Three fixes applied: - Guard mul_by_digit and printbase_mul_by_digit to return integer.zero when the digit is 0 (root cause fix). - Make is_zero/1 recognize denormalized all-zero digit lists as defense in depth. - Replace structural `= integer.zero` checks in rational.m with integer.is_zero/1 calls for robustness. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
committed by
GitHub
parent
b2f1ff63df
commit
fa271a0ec8
@@ -642,6 +642,9 @@ sixteen = i(1, [16]).
|
||||
abs(N) = big_abs(N).
|
||||
|
||||
is_zero(i(0, [])).
|
||||
is_zero(i(_, [D | Ds])) :-
|
||||
D = 0,
|
||||
list.all_true(pred(0::in) is semidet, Ds).
|
||||
|
||||
%---------------------%
|
||||
|
||||
@@ -896,11 +899,15 @@ mul_base_2([H | T]) = [H | mul_base_2(T)].
|
||||
:- func mul_by_digit(digit, integer) = integer.
|
||||
|
||||
mul_by_digit(Digit, i(Len, Digits0)) = Out :-
|
||||
mul_by_digit_2(Digit, Mod, Digits0, Digits),
|
||||
( if Mod = 0 then
|
||||
Out = i(Len, Digits)
|
||||
( if Digit = 0 then
|
||||
Out = integer.zero
|
||||
else
|
||||
Out = i(Len + 1, [Mod | Digits])
|
||||
mul_by_digit_2(Digit, Mod, Digits0, Digits),
|
||||
( if Mod = 0 then
|
||||
Out = i(Len, Digits)
|
||||
else
|
||||
Out = i(Len + 1, [Mod | Digits])
|
||||
)
|
||||
).
|
||||
|
||||
:- pred mul_by_digit_2(digit::in, digit::out, list(digit)::in,
|
||||
@@ -1758,11 +1765,15 @@ printbase_chop(printbase(Base), N, Div, Mod) :-
|
||||
:- func printbase_mul_by_digit(printbase, digit, integer) = integer.
|
||||
|
||||
printbase_mul_by_digit(Base, D, i(Len, Ds)) = Out :-
|
||||
printbase_mul_by_digit_2(Base, D, Div, Ds, DsOut),
|
||||
( if Div = 0 then
|
||||
Out = i(Len, DsOut)
|
||||
( if D = 0 then
|
||||
Out = integer.zero
|
||||
else
|
||||
Out = i(Len + 1, [Div | DsOut])
|
||||
printbase_mul_by_digit_2(Base, D, Div, Ds, DsOut),
|
||||
( if Div = 0 then
|
||||
Out = i(Len, DsOut)
|
||||
else
|
||||
Out = i(Len + 1, [Div | DsOut])
|
||||
)
|
||||
).
|
||||
|
||||
:- pred printbase_mul_by_digit_2(printbase::in, digit::in, digit::out,
|
||||
|
||||
@@ -153,7 +153,7 @@ r(An, Ad) * r(Bn, Bd) = rational_norm(Numer, Denom) :-
|
||||
R1 / R2 = R1 * reciprocal(R2).
|
||||
|
||||
reciprocal(r(Num, Den)) =
|
||||
( if Num = integer.zero then
|
||||
( if integer.is_zero(Num) then
|
||||
func_error($pred, "division by zero")
|
||||
else
|
||||
r(signum(Num) * Den, integer.abs(Num))
|
||||
@@ -164,9 +164,9 @@ abs(r(Num, Den)) = r(integer.abs(Num), Den).
|
||||
:- func rational_norm(integer, integer) = rational.
|
||||
|
||||
rational_norm(Num, Den) = Rat :-
|
||||
( if Den = integer.zero then
|
||||
( if integer.is_zero(Den) then
|
||||
error("rational.rational_norm: division by zero")
|
||||
else if Num = integer.zero then
|
||||
else if integer.is_zero(Num) then
|
||||
Rat = r(integer.zero, integer.one)
|
||||
else
|
||||
G = gcd(Num, Den),
|
||||
@@ -181,14 +181,14 @@ gcd(A, B) = gcd_2(integer.abs(A), integer.abs(B)).
|
||||
|
||||
:- func gcd_2(integer, integer) = integer.
|
||||
|
||||
gcd_2(A, B) = ( if B = integer.zero then A else gcd_2(B, A rem B) ).
|
||||
gcd_2(A, B) = ( if integer.is_zero(B) then A else gcd_2(B, A rem B) ).
|
||||
|
||||
:- func lcm(integer, integer) = integer.
|
||||
|
||||
lcm(A, B) =
|
||||
( if A = integer.zero then
|
||||
( if integer.is_zero(A) then
|
||||
integer.zero
|
||||
else if B = integer.zero then
|
||||
else if integer.is_zero(B) then
|
||||
integer.zero
|
||||
else
|
||||
integer.abs((A // gcd(A, B)) * B)
|
||||
@@ -197,7 +197,7 @@ lcm(A, B) =
|
||||
:- func signum(integer) = integer.
|
||||
|
||||
signum(N) =
|
||||
( if N = integer.zero then
|
||||
( if integer.is_zero(N) then
|
||||
integer.zero
|
||||
else if N < integer.zero then
|
||||
-integer.one
|
||||
@@ -219,7 +219,7 @@ cmp(R1, R2) = Cmp :-
|
||||
|
||||
:- pred is_zero(rational::in) is semidet.
|
||||
|
||||
is_zero(r(integer.zero, _)).
|
||||
is_zero(r(Num, _)) :- integer.is_zero(Num).
|
||||
|
||||
:- pred is_negative(rational::in) is semidet.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user