[m-rev.] for review: implement checked division for uints

Julien Fischer jfischer at opturion.com
Wed May 10 13:10:07 AEST 2017


For review by anyone.

The int module also provides div/2 and mod/2 which round differently for
negative results than / and rem.  The result cannot (obviously) be
negative for uints; do we want to provide div/2 and mod/2 as synonyms for
/ and rem in the uint module?  (mod in particular is the obvious name
for the operation.)

Note: I will update the expected output for 32-bit system before I
commit.

-----------------------------

Implement checked division for uints.

library/uint.m:
      Add (//)/2, (/)/2 and rem/2 for uints.

tests/hard_coded/uint_arith.m
      Enable the division tests.

tests/hard_coded/uint_bitwise.exp:
      Update the expected output.

Julien.

diff --git a/library/uint.m b/library/uint.m
index f757cd78e..a0088ab36 100644
--- a/library/uint.m
+++ b/library/uint.m
@@ -77,8 +77,31 @@
      %
  :- func min(uint, uint) = uint.

+    % Integer division.
+    %
+    % Throws a `math.domain_error' exception if the right operand is zero.
+    %
+:- func (uint::in) // (uint::in) = (uint::uo) is det.
+
+    % (/)/2 is a synonym for (//)/2.
+    %
+:- func (uint::in) / (uint::in) = (uint::uo) is det.
+
+    % unchecked_quotient(X, Y) is the same as X // Y, but the behaviour
+    % is undefined if the right operand is zero.
+    %
  :- func unchecked_quotient(uint::in, uint::in) = (uint::uo) is det.

+    % Remainder.
+    % X rem Y = X - (X // Y) * Y.
+    %
+    % Throws a `math.domain_error/` exception if the right operand is zero.
+    %
+:- func (uint::in) rem (uint::in) = (uint::uo) is det.
+
+    % unchecked_rem(X, Y) is the same as X rem Y, but the behaviour
+    % is undefined if the right operand is zero.
+    %
  :- func unchecked_rem(uint::in, uint::in) = (uint::uo) is det.

      % Left shift.
@@ -244,6 +267,27 @@ cast_to_int(_) = _ :-

  %---------------------------------------------------------------------------%

+:- pragma inline('//'/2).
+X // Y = Div :-
+    ( if Y = cast_from_int(0) then
+        throw(math.domain_error("uint.'//': division by zero"))
+    else
+        Div = unchecked_quotient(X, Y)
+    ).
+
+:- pragma inline('/'/2).
+X / Y = X // Y.
+
+:- pragma inline(rem/2).
+X rem Y = Rem :-
+    ( if Y = cast_from_int(0) then
+        throw(math.domain_error("uint.rem: division by zero"))
+    else
+        Rem = unchecked_rem(X, Y)
+    ).
+
+%---------------------------------------------------------------------------%
+
  X << Y = Result :-
      ( if cast_from_int(Y) < cast_from_int(bits_per_uint) then
          Result = unchecked_left_shift(X, Y)
diff --git a/tests/hard_coded/uint_arith.exp b/tests/hard_coded/uint_arith.exp
index c10e78c52..985cb719f 100644
--- a/tests/hard_coded/uint_arith.exp
+++ b/tests/hard_coded/uint_arith.exp
@@ -199,3 +199,136 @@
  18446744073709551615u * 32u = 18446744073709551584u
  18446744073709551615u * 18446744073709551615u = 1u

+*** Test binary operation '/' ***
+
+0u / 0u = <<exception>>
+0u / 1u = 0u
+0u / 2u = 0u
+0u / 8u = 0u
+0u / 10u = 0u
+0u / 16u = 0u
+0u / 32u = 0u
+0u / 18446744073709551615u = 0u
+1u / 0u = <<exception>>
+1u / 1u = 1u
+1u / 2u = 0u
+1u / 8u = 0u
+1u / 10u = 0u
+1u / 16u = 0u
+1u / 32u = 0u
+1u / 18446744073709551615u = 0u
+2u / 0u = <<exception>>
+2u / 1u = 2u
+2u / 2u = 1u
+2u / 8u = 0u
+2u / 10u = 0u
+2u / 16u = 0u
+2u / 32u = 0u
+2u / 18446744073709551615u = 0u
+8u / 0u = <<exception>>
+8u / 1u = 8u
+8u / 2u = 4u
+8u / 8u = 1u
+8u / 10u = 0u
+8u / 16u = 0u
+8u / 32u = 0u
+8u / 18446744073709551615u = 0u
+10u / 0u = <<exception>>
+10u / 1u = 10u
+10u / 2u = 5u
+10u / 8u = 1u
+10u / 10u = 1u
+10u / 16u = 0u
+10u / 32u = 0u
+10u / 18446744073709551615u = 0u
+16u / 0u = <<exception>>
+16u / 1u = 16u
+16u / 2u = 8u
+16u / 8u = 2u
+16u / 10u = 1u
+16u / 16u = 1u
+16u / 32u = 0u
+16u / 18446744073709551615u = 0u
+32u / 0u = <<exception>>
+32u / 1u = 32u
+32u / 2u = 16u
+32u / 8u = 4u
+32u / 10u = 3u
+32u / 16u = 2u
+32u / 32u = 1u
+32u / 18446744073709551615u = 0u
+18446744073709551615u / 0u = <<exception>>
+18446744073709551615u / 1u = 18446744073709551615u
+18446744073709551615u / 2u = 18446744073709551615u
+18446744073709551615u / 8u = 18446744073709551615u
+18446744073709551615u / 10u = 18446744071991564697u
+18446744073709551615u / 16u = 18446744073709551615u
+18446744073709551615u / 32u = 18446744073709551615u
+18446744073709551615u / 18446744073709551615u = 1u
+
+*** Test binary operation 'rem' ***
+
+0u rem 0u = <<exception>>
+0u rem 1u = 0u
+0u rem 2u = 0u
+0u rem 8u = 0u
+0u rem 10u = 0u
+0u rem 16u = 0u
+0u rem 32u = 0u
+0u rem 18446744073709551615u = 0u
+1u rem 0u = <<exception>>
+1u rem 1u = 0u
+1u rem 2u = 1u
+1u rem 8u = 1u
+1u rem 10u = 1u
+1u rem 16u = 1u
+1u rem 32u = 1u
+1u rem 18446744073709551615u = 1u
+2u rem 0u = <<exception>>
+2u rem 1u = 0u
+2u rem 2u = 0u
+2u rem 8u = 2u
+2u rem 10u = 2u
+2u rem 16u = 2u
+2u rem 32u = 2u
+2u rem 18446744073709551615u = 2u
+8u rem 0u = <<exception>>
+8u rem 1u = 0u
+8u rem 2u = 0u
+8u rem 8u = 0u
+8u rem 10u = 8u
+8u rem 16u = 8u
+8u rem 32u = 8u
+8u rem 18446744073709551615u = 8u
+10u rem 0u = <<exception>>
+10u rem 1u = 0u
+10u rem 2u = 0u
+10u rem 8u = 2u
+10u rem 10u = 0u
+10u rem 16u = 10u
+10u rem 32u = 10u
+10u rem 18446744073709551615u = 10u
+16u rem 0u = <<exception>>
+16u rem 1u = 0u
+16u rem 2u = 0u
+16u rem 8u = 0u
+16u rem 10u = 6u
+16u rem 16u = 0u
+16u rem 32u = 16u
+16u rem 18446744073709551615u = 16u
+32u rem 0u = <<exception>>
+32u rem 1u = 0u
+32u rem 2u = 0u
+32u rem 8u = 0u
+32u rem 10u = 2u
+32u rem 16u = 0u
+32u rem 32u = 0u
+32u rem 18446744073709551615u = 32u
+18446744073709551615u rem 0u = <<exception>>
+18446744073709551615u rem 1u = 0u
+18446744073709551615u rem 2u = 1u
+18446744073709551615u rem 8u = 7u
+18446744073709551615u rem 10u = 5u
+18446744073709551615u rem 16u = 15u
+18446744073709551615u rem 32u = 31u
+18446744073709551615u rem 18446744073709551615u = 0u
diff --git a/tests/hard_coded/uint_arith.m b/tests/hard_coded/uint_arith.m
index 785c159d7..049feaef0 100644
--- a/tests/hard_coded/uint_arith.m
+++ b/tests/hard_coded/uint_arith.m
@@ -29,8 +29,10 @@ main(!IO) :-
      run_binop_test((func(X, Y) = X - Y), "-", !IO),
      io.nl(!IO),
      run_binop_test((func(X, Y) = X * Y), "*", !IO),
-    io.nl(!IO).
-    %run_binop_test(uint.(/), "/", !IO).   % NYI.
+    io.nl(!IO),
+    run_binop_test(uint.(/), "/", !IO),
+    io.nl(!IO),
+    run_binop_test(uint.(rem), "rem", !IO).

  :- pred run_binop_test((func(uint, uint) = uint)::in, string::in,
      io::di, io::uo) is cc_multi.


More information about the reviews mailing list