1
0
mirror of https://github.com/golang/go synced 2024-11-18 00:54:45 -07:00

math/big: normalize unitialized denominators ASAP

A Rat is represented via a quotient a/b where a and b are Int values.
To make it possible to use an uninitialized Rat value (with a and b
uninitialized and thus == 0), the implementation treats a 0 denominator
as 1.

For each operation we check if the denominator is 0, and then treat
it as 1 (if necessary). Operations that create a new Rat result,
normalize that value such that a result denominator 1 is represened
as 0 again.

This CL changes this behavior slightly: 0 denominators are still
interpreted as 1, but whenever we (safely) can, we set an uninitialized
0 denominator to 1. This simplifies the code overall.

Also: Improved some doc strings.

Preparation for addressing issue #33792.

Updates #33792.

Change-Id: I3040587c8d0dad2e840022f96ca027d8470878a0
Reviewed-on: https://go-review.googlesource.com/c/go/+/202997
Run-TryBot: Robert Griesemer <gri@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
This commit is contained in:
Robert Griesemer 2019-10-23 14:22:32 -07:00
parent 758eb020f7
commit 4412181e7c

View File

@ -22,7 +22,9 @@ import (
// of Rats are not supported and may lead to errors.
type Rat struct {
// To make zero values for Rat work w/o initialization,
// a zero value of b (len(b) == 0) acts like b == 1.
// a zero value of b (len(b) == 0) acts like b == 1. At
// the earliest opportunity (when an assignment to the Rat
// is made), such uninitialized denominators are set to 1.
// a.neg determines the sign of the Rat, b.neg is ignored.
a, b Int
}
@ -297,6 +299,7 @@ func (x *Rat) Float64() (f float64, exact bool) {
}
// SetFrac sets z to a/b and returns z.
// If b == 0, SetFrac panics.
func (z *Rat) SetFrac(a, b *Int) *Rat {
z.a.neg = a.neg != b.neg
babs := b.abs
@ -312,11 +315,12 @@ func (z *Rat) SetFrac(a, b *Int) *Rat {
}
// SetFrac64 sets z to a/b and returns z.
// If b == 0, SetFrac64 panics.
func (z *Rat) SetFrac64(a, b int64) *Rat {
z.a.SetInt64(a)
if b == 0 {
panic("division by zero")
}
z.a.SetInt64(a)
if b < 0 {
b = -b
z.a.neg = !z.a.neg
@ -328,21 +332,21 @@ func (z *Rat) SetFrac64(a, b int64) *Rat {
// SetInt sets z to x (by making a copy of x) and returns z.
func (z *Rat) SetInt(x *Int) *Rat {
z.a.Set(x)
z.b.abs = z.b.abs[:0]
z.b.abs = z.b.abs.setWord(1)
return z
}
// SetInt64 sets z to x and returns z.
func (z *Rat) SetInt64(x int64) *Rat {
z.a.SetInt64(x)
z.b.abs = z.b.abs[:0]
z.b.abs = z.b.abs.setWord(1)
return z
}
// SetUint64 sets z to x and returns z.
func (z *Rat) SetUint64(x uint64) *Rat {
z.a.SetUint64(x)
z.b.abs = z.b.abs[:0]
z.b.abs = z.b.abs.setWord(1)
return z
}
@ -352,6 +356,9 @@ func (z *Rat) Set(x *Rat) *Rat {
z.a.Set(&x.a)
z.b.Set(&x.b)
}
if len(z.b.abs) == 0 {
z.b.abs = z.b.abs.setWord(1)
}
return z
}
@ -370,20 +377,13 @@ func (z *Rat) Neg(x *Rat) *Rat {
}
// Inv sets z to 1/x and returns z.
// If x == 0, Inv panics.
func (z *Rat) Inv(x *Rat) *Rat {
if len(x.a.abs) == 0 {
panic("division by zero")
}
z.Set(x)
a := z.b.abs
if len(a) == 0 {
a = a.set(natOne) // materialize numerator (a is part of z!)
}
b := z.a.abs
if b.cmp(natOne) == 0 {
b = b[:0] // normalize denominator
}
z.a.abs, z.b.abs = a, b // sign doesn't change
z.a.abs, z.b.abs = z.b.abs, z.a.abs
return z
}
@ -426,25 +426,20 @@ func (x *Rat) Denom() *Int {
func (z *Rat) norm() *Rat {
switch {
case len(z.a.abs) == 0:
// z == 0 - normalize sign and denominator
// z == 0; normalize sign and denominator
z.a.neg = false
z.b.abs = z.b.abs[:0]
fallthrough
case len(z.b.abs) == 0:
// z is normalized int - nothing to do
case z.b.abs.cmp(natOne) == 0:
// z is int - normalize denominator
z.b.abs = z.b.abs[:0]
// z is integer; normalize denominator
z.b.abs = z.b.abs.setWord(1)
default:
// z is fraction; normalize numerator and denominator
neg := z.a.neg
z.a.neg = false
z.b.neg = false
if f := NewInt(0).lehmerGCD(nil, nil, &z.a, &z.b); f.Cmp(intOne) != 0 {
z.a.abs, _ = z.a.abs.div(nil, z.a.abs, f.abs)
z.b.abs, _ = z.b.abs.div(nil, z.b.abs, f.abs)
if z.b.abs.cmp(natOne) == 0 {
// z is int - normalize denominator
z.b.abs = z.b.abs[:0]
}
}
z.a.neg = neg
}
@ -456,6 +451,8 @@ func (z *Rat) norm() *Rat {
// returns z.
func mulDenom(z, x, y nat) nat {
switch {
case len(x) == 0 && len(y) == 0:
return z.setWord(1)
case len(x) == 0:
return z.set(y)
case len(y) == 0:
@ -511,10 +508,14 @@ func (z *Rat) Sub(x, y *Rat) *Rat {
// Mul sets z to the product x*y and returns z.
func (z *Rat) Mul(x, y *Rat) *Rat {
if x == y {
// a squared Rat is positive and can't be reduced
// a squared Rat is positive and can't be reduced (no need to call norm())
z.a.neg = false
z.a.abs = z.a.abs.sqr(x.a.abs)
z.b.abs = z.b.abs.sqr(x.b.abs)
if len(x.b.abs) == 0 {
z.b.abs = z.b.abs.setWord(1)
} else {
z.b.abs = z.b.abs.sqr(x.b.abs)
}
return z
}
z.a.Mul(&x.a, &y.a)
@ -523,7 +524,7 @@ func (z *Rat) Mul(x, y *Rat) *Rat {
}
// Quo sets z to the quotient x/y and returns z.
// If y == 0, a division-by-zero run-time panic occurs.
// If y == 0, Quo panics.
func (z *Rat) Quo(x, y *Rat) *Rat {
if len(y.a.abs) == 0 {
panic("division by zero")