mirror of
https://github.com/golang/go.git
synced 2026-06-28 03:40:37 +00:00
math/big: move Int.Divide and corresponding test function up (cleanup)
This simply moves these two functions next/below the group of related functions (i.e., Int.Divide is now with the other division functions). This makes it easier to see its code in context when looking at the source. No other code changes. Follow-up on CL 729860. Change-Id: Iccfcd014507fa8a68a7d1290ba246b714f8a66a8 Reviewed-on: https://go-review.googlesource.com/c/go/+/779540 Auto-Submit: Robert Griesemer <gri@google.com> Reviewed-by: Robert Griesemer <gri@google.com> Reviewed-by: Neal Patel <neal@golang.org> Reviewed-by: Neal Patel <nealpatel@google.com> LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
parent
5f47eb0cdf
commit
93da30397d
2 changed files with 151 additions and 151 deletions
|
|
@ -371,6 +371,89 @@ func (z *Int) DivMod(x, y, m *Int) (*Int, *Int) {
|
|||
return z, m
|
||||
}
|
||||
|
||||
// Rounding modes that determine how the integer quotient is adjusted in an integer division.
|
||||
// See Daan Leijen, “Division and Modulus for Computer Scientists”, for details.
|
||||
const (
|
||||
Trunc = ToZero // T-division (same as Go division)
|
||||
Floor = ToNegativeInf // F-division
|
||||
Round = ToNearestEven // R-division
|
||||
Ceil = ToPositiveInf // C-division
|
||||
)
|
||||
|
||||
// Divide computes the integer quotient q and remainder r such that
|
||||
//
|
||||
// q = f(x/y)
|
||||
// r = x - y*q
|
||||
//
|
||||
// where f is described by the rounding mode,
|
||||
// which must be one of [Trunc], [Floor], [Round] or [Ceil].
|
||||
// Divide sets z to q if z != nil, updates r if r != nil,
|
||||
// and returns the pair (z, r) if y != 0.
|
||||
// If y == 0, a division-by-zero run-time panic occurs.
|
||||
func (z *Int) Divide(x, y, r *Int, mode RoundingMode) (*Int, *Int) {
|
||||
// TODO: optimize the code where z or r is nil
|
||||
var z_abs nat
|
||||
if z != nil {
|
||||
z_abs = z.abs
|
||||
}
|
||||
var r_neg bool
|
||||
var r_abs nat
|
||||
if r != nil {
|
||||
r_abs = r.abs
|
||||
}
|
||||
y_abs := y.abs // save y
|
||||
if z == y || alias(z_abs, y.abs) {
|
||||
y_abs = nat(nil).set(y.abs)
|
||||
}
|
||||
neg := x.neg != y.neg
|
||||
z_abs, r_abs = z_abs.div(nil, r_abs, x.abs, y.abs)
|
||||
if len(r_abs) > 0 {
|
||||
switch mode {
|
||||
case Trunc:
|
||||
r_neg = x.neg
|
||||
case Floor:
|
||||
r_neg = y.neg
|
||||
if neg {
|
||||
z_abs = z_abs.add(z_abs, natOne)
|
||||
r_abs = r_abs.sub(y_abs, r_abs)
|
||||
}
|
||||
case Ceil:
|
||||
r_neg = !y.neg
|
||||
if !neg {
|
||||
z_abs = z_abs.add(z_abs, natOne)
|
||||
r_abs = r_abs.sub(y_abs, r_abs)
|
||||
}
|
||||
case Round:
|
||||
switch nat(nil).mul(nil, r_abs, natTwo).cmp(y_abs) {
|
||||
case -1:
|
||||
r_neg = x.neg
|
||||
case 0:
|
||||
even := len(z_abs) == 0 || z_abs[0]&1 == 0
|
||||
if even {
|
||||
r_neg = x.neg
|
||||
break
|
||||
}
|
||||
fallthrough
|
||||
case 1:
|
||||
r_neg = !x.neg
|
||||
z_abs = z_abs.add(z_abs, natOne)
|
||||
r_abs = r_abs.sub(y_abs, r_abs)
|
||||
}
|
||||
default:
|
||||
panic("unsupported rounding mode")
|
||||
}
|
||||
}
|
||||
if z != nil {
|
||||
z.abs = z_abs
|
||||
z.neg = neg && len(z_abs) > 0 // 0 has no sign
|
||||
}
|
||||
if r != nil {
|
||||
r.abs = r_abs
|
||||
r.neg = r_neg
|
||||
}
|
||||
return z, r
|
||||
}
|
||||
|
||||
// Cmp compares x and y and returns:
|
||||
// - -1 if x < y;
|
||||
// - 0 if x == y;
|
||||
|
|
@ -1308,86 +1391,3 @@ func (z *Int) Sqrt(x *Int) *Int {
|
|||
z.abs = z.abs.sqrt(nil, x.abs)
|
||||
return z
|
||||
}
|
||||
|
||||
// Rounding modes that determine how the integer quotient is adjusted in an integer division.
|
||||
// See Daan Leijen, “Division and Modulus for Computer Scientists”, for details.
|
||||
const (
|
||||
Trunc = ToZero // T-division (same as Go division)
|
||||
Floor = ToNegativeInf // F-division
|
||||
Round = ToNearestEven // R-division
|
||||
Ceil = ToPositiveInf // C-division
|
||||
)
|
||||
|
||||
// Divide computes the integer quotient q and remainder r such that
|
||||
//
|
||||
// q = f(x/y)
|
||||
// r = x - y*q
|
||||
//
|
||||
// where f is described by the rounding mode,
|
||||
// which must be one of [Trunc], [Floor], [Round] or [Ceil].
|
||||
// Divide sets z to q if z != nil, updates r if r != nil,
|
||||
// and returns the pair (z, r) if y != 0.
|
||||
// If y == 0, a division-by-zero run-time panic occurs.
|
||||
func (z *Int) Divide(x, y, r *Int, mode RoundingMode) (*Int, *Int) {
|
||||
// TODO: optimize the code where z or r is nil
|
||||
var z_abs nat
|
||||
if z != nil {
|
||||
z_abs = z.abs
|
||||
}
|
||||
var r_neg bool
|
||||
var r_abs nat
|
||||
if r != nil {
|
||||
r_abs = r.abs
|
||||
}
|
||||
y_abs := y.abs // save y
|
||||
if z == y || alias(z_abs, y.abs) {
|
||||
y_abs = nat(nil).set(y.abs)
|
||||
}
|
||||
neg := x.neg != y.neg
|
||||
z_abs, r_abs = z_abs.div(nil, r_abs, x.abs, y.abs)
|
||||
if len(r_abs) > 0 {
|
||||
switch mode {
|
||||
case Trunc:
|
||||
r_neg = x.neg
|
||||
case Floor:
|
||||
r_neg = y.neg
|
||||
if neg {
|
||||
z_abs = z_abs.add(z_abs, natOne)
|
||||
r_abs = r_abs.sub(y_abs, r_abs)
|
||||
}
|
||||
case Ceil:
|
||||
r_neg = !y.neg
|
||||
if !neg {
|
||||
z_abs = z_abs.add(z_abs, natOne)
|
||||
r_abs = r_abs.sub(y_abs, r_abs)
|
||||
}
|
||||
case Round:
|
||||
switch nat(nil).mul(nil, r_abs, natTwo).cmp(y_abs) {
|
||||
case -1:
|
||||
r_neg = x.neg
|
||||
case 0:
|
||||
even := len(z_abs) == 0 || z_abs[0]&1 == 0
|
||||
if even {
|
||||
r_neg = x.neg
|
||||
break
|
||||
}
|
||||
fallthrough
|
||||
case 1:
|
||||
r_neg = !x.neg
|
||||
z_abs = z_abs.add(z_abs, natOne)
|
||||
r_abs = r_abs.sub(y_abs, r_abs)
|
||||
}
|
||||
default:
|
||||
panic("unsupported rounding mode")
|
||||
}
|
||||
}
|
||||
if z != nil {
|
||||
z.abs = z_abs
|
||||
z.neg = neg && len(z_abs) > 0 // 0 has no sign
|
||||
}
|
||||
if r != nil {
|
||||
r.abs = r_abs
|
||||
r.neg = r_neg
|
||||
}
|
||||
return z, r
|
||||
}
|
||||
|
|
|
|||
|
|
@ -505,6 +505,74 @@ func BenchmarkQuoRem(b *testing.B) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestIntDivide(t *testing.T) {
|
||||
x := new(Int)
|
||||
y := new(Int)
|
||||
q := new(Int)
|
||||
r := new(Int)
|
||||
f := new(Int)
|
||||
qGot := new(Int)
|
||||
rGot := new(Int)
|
||||
|
||||
check := func(i, j, q_ int64, mode RoundingMode, modeName string) {
|
||||
x.SetInt64(i)
|
||||
y.SetInt64(j)
|
||||
q.SetInt64(q_)
|
||||
r.SetInt64(i - j*q_)
|
||||
|
||||
// The quotient remains the same irrespective of scaling factor f,
|
||||
// everything else gets scaled by f; f is set by the caller.
|
||||
x.Mul(x, f)
|
||||
y.Mul(y, f)
|
||||
r.Mul(r, f)
|
||||
|
||||
qGot, rGot = qGot.Divide(x, y, rGot, mode)
|
||||
if qGot.Cmp(q) != 0 || rGot.Cmp(r) != 0 {
|
||||
t.Errorf("%v(%v/%v): got q = %v, r = %v; want q = %v, r = %v", modeName, x, y, qGot, rGot, q, r)
|
||||
}
|
||||
|
||||
// nil remainder result
|
||||
qGot, _ = qGot.Divide(x, y, nil, mode)
|
||||
if qGot.Cmp(q) != 0 {
|
||||
t.Errorf("%v(%v/%v): got q = %v; want q = %v", modeName, x, y, qGot, q)
|
||||
}
|
||||
|
||||
// nil quotient result
|
||||
_, rGot = (*Int)(nil).Divide(x, y, rGot, mode)
|
||||
if rGot.Cmp(r) != 0 {
|
||||
t.Errorf("%v(%v/%v): got r = %v; want r = %v", modeName, x, y, rGot, r)
|
||||
}
|
||||
|
||||
// nil quotient and remainder must not panic
|
||||
(*Int)(nil).Divide(x, y, nil, mode)
|
||||
}
|
||||
|
||||
// test each case with different scaling factors f
|
||||
for _, s := range []string{
|
||||
"1",
|
||||
"1234",
|
||||
"99991",
|
||||
"1234567890",
|
||||
"12345678901234567890",
|
||||
} {
|
||||
f.SetString(s, 10)
|
||||
const n int64 = 10
|
||||
for i := -n; i <= n; i++ {
|
||||
for j := -n; j <= n; j++ {
|
||||
if j == 0 {
|
||||
continue
|
||||
}
|
||||
z := float64(i) / float64(j)
|
||||
check(i, j, i/j, Trunc, "trunc") // T-division is regular Go integer division
|
||||
check(i, j, int64(math.Trunc(z)), Trunc, "trunc")
|
||||
check(i, j, int64(math.Floor(z)), Floor, "floor")
|
||||
check(i, j, int64(math.Ceil(z)), Ceil, "ceil")
|
||||
check(i, j, int64(math.RoundToEven(z)), Round, "round")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var bitLenTests = []struct {
|
||||
in string
|
||||
out int
|
||||
|
|
@ -2011,71 +2079,3 @@ func TestFloat64(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIntDivide(t *testing.T) {
|
||||
x := new(Int)
|
||||
y := new(Int)
|
||||
q := new(Int)
|
||||
r := new(Int)
|
||||
f := new(Int)
|
||||
qGot := new(Int)
|
||||
rGot := new(Int)
|
||||
|
||||
check := func(i, j, q_ int64, mode RoundingMode, modeName string) {
|
||||
x.SetInt64(i)
|
||||
y.SetInt64(j)
|
||||
q.SetInt64(q_)
|
||||
r.SetInt64(i - j*q_)
|
||||
|
||||
// The quotient remains the same irrespective of scaling factor f,
|
||||
// everything else gets scaled by f; f is set by the caller.
|
||||
x.Mul(x, f)
|
||||
y.Mul(y, f)
|
||||
r.Mul(r, f)
|
||||
|
||||
qGot, rGot = qGot.Divide(x, y, rGot, mode)
|
||||
if qGot.Cmp(q) != 0 || rGot.Cmp(r) != 0 {
|
||||
t.Errorf("%v(%v/%v): got q = %v, r = %v; want q = %v, r = %v", modeName, x, y, qGot, rGot, q, r)
|
||||
}
|
||||
|
||||
// nil remainder result
|
||||
qGot, _ = qGot.Divide(x, y, nil, mode)
|
||||
if qGot.Cmp(q) != 0 {
|
||||
t.Errorf("%v(%v/%v): got q = %v; want q = %v", modeName, x, y, qGot, q)
|
||||
}
|
||||
|
||||
// nil quotient result
|
||||
_, rGot = (*Int)(nil).Divide(x, y, rGot, mode)
|
||||
if rGot.Cmp(r) != 0 {
|
||||
t.Errorf("%v(%v/%v): got r = %v; want r = %v", modeName, x, y, rGot, r)
|
||||
}
|
||||
|
||||
// nil quotient and remainder must not panic
|
||||
(*Int)(nil).Divide(x, y, nil, mode)
|
||||
}
|
||||
|
||||
// test each case with different scaling factors f
|
||||
for _, s := range []string{
|
||||
"1",
|
||||
"1234",
|
||||
"99991",
|
||||
"1234567890",
|
||||
"12345678901234567890",
|
||||
} {
|
||||
f.SetString(s, 10)
|
||||
const n int64 = 10
|
||||
for i := -n; i <= n; i++ {
|
||||
for j := -n; j <= n; j++ {
|
||||
if j == 0 {
|
||||
continue
|
||||
}
|
||||
z := float64(i) / float64(j)
|
||||
check(i, j, i/j, Trunc, "trunc") // T-division is regular Go integer division
|
||||
check(i, j, int64(math.Trunc(z)), Trunc, "trunc")
|
||||
check(i, j, int64(math.Floor(z)), Floor, "floor")
|
||||
check(i, j, int64(math.Ceil(z)), Ceil, "ceil")
|
||||
check(i, j, int64(math.RoundToEven(z)), Round, "round")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue