]> Cypherpunks repositories - gostls13.git/commitdiff
math/bits: add Rem, Rem32, Rem64
authorAlberto Donizetti <alb.donizetti@gmail.com>
Sat, 28 Sep 2019 17:02:15 +0000 (19:02 +0200)
committerAlberto Donizetti <alb.donizetti@gmail.com>
Fri, 18 Oct 2019 13:47:36 +0000 (13:47 +0000)
The Div functions in math/bits (Div, Div32, and Div64) compute both
quotients and remainders, but they panic if the quotients do not not
fit a 32/64 uint.

Since, on the other hand, the remainder will always fit the size of
the divisor, it is useful to have Div variants that only compute the
remainder, and don't panic on a quotient overflow.

This change adds to the math/bits package three new functions:

  Rem(hi, lo, y uint) uint
  Rem32(hi, lo, y uint32) uint32
  Rem64(hi, lo, y uint64) uint64

which can be used to compute (hi,lo)%y even when the quotient
overflows the uint size.

Fixes #28970

Change-Id: I119948429f737670c5e5ceb8756121e6a738dbdc
Reviewed-on: https://go-review.googlesource.com/c/go/+/197838
Run-TryBot: Alberto Donizetti <alb.donizetti@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
src/math/bits/bits.go
src/math/bits/bits_test.go

index 385c0648e35d2c762a9f88c75f03c97f961662d1..879ef2da5414f5ac2f6c2d22f07efeadb6a9d4b0 100644 (file)
@@ -555,3 +555,34 @@ func Div64(hi, lo, y uint64) (quo, rem uint64) {
 
        return q1*two32 + q0, (un21*two32 + un0 - q0*y) >> s
 }
+
+// Rem returns the remainder of (hi, lo) divided by y. Rem panics for
+// y == 0 (division by zero) but, unlike Div, it doesn't panic on a
+// quotient overflow.
+func Rem(hi, lo, y uint) uint {
+       if UintSize == 32 {
+               return uint(Rem32(uint32(hi), uint32(lo), uint32(y)))
+       }
+       return uint(Rem64(uint64(hi), uint64(lo), uint64(y)))
+}
+
+// Rem32 returns the remainder of (hi, lo) divided by y. Rem32 panics
+// for y == 0 (division by zero) but, unlike Div32, it doesn't panic
+// on a quotient overflow.
+func Rem32(hi, lo, y uint32) uint32 {
+       return uint32((uint64(hi)<<32 | uint64(lo)) % uint64(y))
+}
+
+// Rem64 returns the remainder of (hi, lo) divided by y. Rem64 panics
+// for y == 0 (division by zero) but, unlike Div64, it doesn't panic
+// on a quotient overflow.
+func Rem64(hi, lo, y uint64) uint64 {
+       // We scale down hi so that hi < y, then use Div64 to compute the
+       // rem with the guarantee that it won't panic on quotient overflow.
+       // Given that
+       //   hi ≡ hi%y    (mod y)
+       // we have
+       //   hi<<64 + lo ≡ (hi%y)<<64 + lo    (mod y)
+       _, rem := Div64(hi%y, lo, y)
+       return rem
+}
index afdfd393bb38b8ca8efe1f0f9407e9a5fff8098d..c0f43093d9b5f975bc0e600fb59aa8c3b087bbbc 100644 (file)
@@ -984,6 +984,76 @@ func TestDiv64PanicZero(t *testing.T) {
        t.Errorf("undefined q, r = %v, %v calculated when Div64 should have panicked", q, r)
 }
 
+func TestRem32(t *testing.T) {
+       // Sanity check: for non-oveflowing dividends, the result is the
+       // same as the rem returned by Div32
+       hi, lo, y := uint32(510510), uint32(9699690), uint32(510510+1) // ensure hi < y
+       for i := 0; i < 1000; i++ {
+               r := Rem32(hi, lo, y)
+               _, r2 := Div32(hi, lo, y)
+               if r != r2 {
+                       t.Errorf("Rem32(%v, %v, %v) returned %v, but Div32 returned rem %v", hi, lo, y, r, r2)
+               }
+               y += 13
+       }
+}
+
+func TestRem32Overflow(t *testing.T) {
+       // To trigger a quotient overflow, we need y <= hi
+       hi, lo, y := uint32(510510), uint32(9699690), uint32(7)
+       for i := 0; i < 1000; i++ {
+               r := Rem32(hi, lo, y)
+               _, r2 := Div64(0, uint64(hi)<<32|uint64(lo), uint64(y))
+               if r != uint32(r2) {
+                       t.Errorf("Rem32(%v, %v, %v) returned %v, but Div64 returned rem %v", hi, lo, y, r, r2)
+               }
+               y += 13
+       }
+}
+
+func TestRem64(t *testing.T) {
+       // Sanity check: for non-oveflowing dividends, the result is the
+       // same as the rem returned by Div64
+       hi, lo, y := uint64(510510), uint64(9699690), uint64(510510+1) // ensure hi < y
+       for i := 0; i < 1000; i++ {
+               r := Rem64(hi, lo, y)
+               _, r2 := Div64(hi, lo, y)
+               if r != r2 {
+                       t.Errorf("Rem64(%v, %v, %v) returned %v, but Div64 returned rem %v", hi, lo, y, r, r2)
+               }
+               y += 13
+       }
+}
+
+func TestRem64Overflow(t *testing.T) {
+       Rem64Tests := []struct {
+               hi, lo, y uint64
+               rem       uint64
+       }{
+               // Testcases computed using Python 3, as:
+               //   >>> hi = 42; lo = 1119; y = 42
+               //   >>> ((hi<<64)+lo) % y
+               {42, 1119, 42, 27},
+               {42, 1119, 38, 9},
+               {42, 1119, 26, 23},
+               {469, 0, 467, 271},
+               {469, 0, 113, 58},
+               {111111, 111111, 1171, 803},
+               {3968194946088682615, 3192705705065114702, 1000037, 56067},
+       }
+
+       for _, rt := range Rem64Tests {
+               if rt.hi < rt.y {
+                       t.Fatalf("Rem64(%v, %v, %v) is not a test with quo overflow", rt.hi, rt.lo, rt.y)
+               }
+               rem := Rem64(rt.hi, rt.lo, rt.y)
+               if rem != rt.rem {
+                       t.Errorf("Rem64(%v, %v, %v) returned %v, wanted %v",
+                               rt.hi, rt.lo, rt.y, rem, rt.rem)
+               }
+       }
+}
+
 func BenchmarkAdd(b *testing.B) {
        var z, c uint
        for i := 0; i < b.N; i++ {