Commit ca0be6f8 authored by Robert Griesemer's avatar Robert Griesemer

math/big: clean up Float.SetPrec, use shorter internal representation

Change-Id: I9b78085adc12cbd240d0b8b48db6810ddb2aeadd
Reviewed-on: https://go-review.googlesource.com/5991Reviewed-by: 's avatarAlan Donovan <adonovan@google.com>
parent c20a018d
...@@ -61,7 +61,7 @@ type Float struct { ...@@ -61,7 +61,7 @@ type Float struct {
neg bool neg bool
mant nat mant nat
exp int32 exp int32
prec uint // TODO(gri) make this a 32bit field prec uint32
} }
// TODO(gri) provide a couple of Example tests showing typical Float intialization // TODO(gri) provide a couple of Example tests showing typical Float intialization
...@@ -77,8 +77,9 @@ type Float struct { ...@@ -77,8 +77,9 @@ type Float struct {
// values have an empty mantissa and a 0 or infExp exponent, respectively. // values have an empty mantissa and a 0 or infExp exponent, respectively.
const ( const (
MaxExp = math.MaxInt32 // largest supported exponent magnitude MaxExp = math.MaxInt32 // largest supported exponent magnitude
infExp = -MaxExp - 1 // exponent for Inf values infExp = -MaxExp - 1 // exponent for Inf values
MaxPrec = math.MaxUint32 // largest (theoretically) supported precision; likely memory-limited
) )
// NewInf returns a new infinite Float value with value +Inf (sign >= 0), // NewInf returns a new infinite Float value with value +Inf (sign >= 0),
...@@ -150,11 +151,32 @@ func (mode RoundingMode) String() string { ...@@ -150,11 +151,32 @@ func (mode RoundingMode) String() string {
// SetPrec sets z's precision to prec and returns the (possibly) rounded // SetPrec sets z's precision to prec and returns the (possibly) rounded
// value of z. Rounding occurs according to z's rounding mode if the mantissa // value of z. Rounding occurs according to z's rounding mode if the mantissa
// cannot be represented in prec bits without loss of precision. // cannot be represented in prec bits without loss of precision.
// If prec == 0, the result is ±0 for finite z, and ±Inf for infinite z,
// with the sign set according to z. If prec > MaxPrec, it is set to MaxPrec.
func (z *Float) SetPrec(prec uint) *Float { func (z *Float) SetPrec(prec uint) *Float {
z.acc = Exact // optimistically assume no rounding is needed
// handle special case
if prec == 0 {
z.prec = 0
if len(z.mant) != 0 {
// truncate and compute accuracy
z.mant = z.mant[:0]
z.exp = 0
acc := Below
if z.neg {
acc = Above
}
z.acc = acc
}
return z
}
// general case
if prec > MaxPrec {
prec = MaxPrec
}
old := z.prec old := z.prec
z.acc = Exact z.prec = uint32(prec)
z.prec = prec if z.prec < old {
if prec < old {
z.round(0) z.round(0)
} }
return z return z
...@@ -259,7 +281,7 @@ func (x *Float) IsInt() bool { ...@@ -259,7 +281,7 @@ func (x *Float) IsInt() bool {
return len(x.mant) == 0 && x.exp != infExp return len(x.mant) == 0 && x.exp != infExp
} }
// x.exp > 0 // x.exp > 0
return x.prec <= uint(x.exp) || x.minPrec() <= uint(x.exp) // not enough bits for fractional mantissa return x.prec <= uint32(x.exp) || x.minPrec() <= uint(x.exp) // not enough bits for fractional mantissa
} }
// IsInf reports whether x is an infinity, according to sign. // IsInf reports whether x is an infinity, according to sign.
...@@ -320,7 +342,7 @@ func (z *Float) round(sbit uint) { ...@@ -320,7 +342,7 @@ func (z *Float) round(sbit uint) {
z.acc = Exact z.acc = Exact
// handle zero and Inf // handle zero and Inf
m := uint(len(z.mant)) // present mantissa length in words m := uint32(len(z.mant)) // present mantissa length in words
if m == 0 { if m == 0 {
if z.exp != infExp { if z.exp != infExp {
z.exp = 0 z.exp = 0
...@@ -351,8 +373,8 @@ func (z *Float) round(sbit uint) { ...@@ -351,8 +373,8 @@ func (z *Float) round(sbit uint) {
// 1 1 > 0.5, < 1.0 // 1 1 > 0.5, < 1.0
// bits > z.prec: mantissa too large => round // bits > z.prec: mantissa too large => round
r := bits - z.prec - 1 // rounding bit position; r >= 0 r := uint(bits - z.prec - 1) // rounding bit position; r >= 0
rbit := z.mant.bit(r) // rounding bit rbit := z.mant.bit(r) // rounding bit
if sbit == 0 { if sbit == 0 {
sbit = z.mant.sticky(r) sbit = z.mant.sticky(r)
} }
...@@ -567,9 +589,9 @@ func (z *Float) SetInt(x *Int) *Float { ...@@ -567,9 +589,9 @@ func (z *Float) SetInt(x *Int) *Float {
// TODO(gri) can be more efficient if z.prec > 0 // TODO(gri) can be more efficient if z.prec > 0
// but small compared to the size of x, or if there // but small compared to the size of x, or if there
// are many trailing 0's. // are many trailing 0's.
bits := uint(x.BitLen()) bits := uint32(x.BitLen())
if z.prec == 0 { if z.prec == 0 {
z.prec = umax(bits, 64) z.prec = umax32(bits, 64)
} }
z.acc = Exact z.acc = Exact
z.neg = x.neg z.neg = x.neg
...@@ -599,7 +621,7 @@ func (z *Float) SetRat(x *Rat) *Float { ...@@ -599,7 +621,7 @@ func (z *Float) SetRat(x *Rat) *Float {
a.SetInt(x.Num()) a.SetInt(x.Num())
b.SetInt(x.Denom()) b.SetInt(x.Denom())
if z.prec == 0 { if z.prec == 0 {
z.prec = umax(a.prec, b.prec) z.prec = umax32(a.prec, b.prec)
} }
return z.Quo(&a, &b) return z.Quo(&a, &b)
} }
...@@ -1126,7 +1148,7 @@ func (z *Float) Add(x, y *Float) *Float { ...@@ -1126,7 +1148,7 @@ func (z *Float) Add(x, y *Float) *Float {
} }
if z.prec == 0 { if z.prec == 0 {
z.prec = umax(x.prec, y.prec) z.prec = umax32(x.prec, y.prec)
} }
// TODO(gri) what about -0? // TODO(gri) what about -0?
...@@ -1167,7 +1189,7 @@ func (z *Float) Sub(x, y *Float) *Float { ...@@ -1167,7 +1189,7 @@ func (z *Float) Sub(x, y *Float) *Float {
} }
if z.prec == 0 { if z.prec == 0 {
z.prec = umax(x.prec, y.prec) z.prec = umax32(x.prec, y.prec)
} }
// TODO(gri) what about -0? // TODO(gri) what about -0?
...@@ -1207,7 +1229,7 @@ func (z *Float) Mul(x, y *Float) *Float { ...@@ -1207,7 +1229,7 @@ func (z *Float) Mul(x, y *Float) *Float {
} }
if z.prec == 0 { if z.prec == 0 {
z.prec = umax(x.prec, y.prec) z.prec = umax32(x.prec, y.prec)
} }
// TODO(gri) handle Inf // TODO(gri) handle Inf
...@@ -1236,7 +1258,7 @@ func (z *Float) Quo(x, y *Float) *Float { ...@@ -1236,7 +1258,7 @@ func (z *Float) Quo(x, y *Float) *Float {
} }
if z.prec == 0 { if z.prec == 0 {
z.prec = umax(x.prec, y.prec) z.prec = umax32(x.prec, y.prec)
} }
// TODO(gri) handle Inf // TODO(gri) handle Inf
...@@ -1337,7 +1359,7 @@ func (x *Float) Cmp(y *Float) int { ...@@ -1337,7 +1359,7 @@ func (x *Float) Cmp(y *Float) int {
return 0 return 0
} }
func umax(x, y uint) uint { func umax32(x, y uint32) uint32 {
if x > y { if x > y {
return x return x
} }
......
...@@ -104,6 +104,58 @@ func makeFloat(s string) *Float { ...@@ -104,6 +104,58 @@ func makeFloat(s string) *Float {
return &x return &x
} }
func TestFloatSetPrec(t *testing.T) {
for _, test := range []struct {
x string
prec uint
want string
acc Accuracy
}{
// prec 0
{"0", 0, "0", Exact},
{"-0", 0, "-0", Exact},
{"-Inf", 0, "-Inf", Exact},
{"+Inf", 0, "+Inf", Exact},
{"123", 0, "0", Below},
{"-123", 0, "-0", Above},
// prec at upper limit
{"0", MaxPrec, "0", Exact},
{"0", MaxPrec + 1, "0", Exact},
{"-0", MaxPrec, "-0", Exact},
{"-0", MaxPrec + 1, "-0", Exact},
{"-Inf", MaxPrec, "-Inf", Exact},
{"+Inf", MaxPrec + 1, "+Inf", Exact},
{"-Inf", MaxPrec, "-Inf", Exact},
{"+Inf", MaxPrec + 1, "+Inf", Exact},
// just a few regular cases - general rounding is tested elsewhere
{"1.5", 1, "2", Above},
{"-1.5", 1, "-2", Below},
{"123", 1e6, "123", Exact},
{"-123", 1e6, "-123", Exact},
} {
x := makeFloat(test.x).SetPrec(test.prec)
prec := test.prec
if prec > MaxPrec {
prec = MaxPrec
}
if got := x.Prec(); got != prec {
t.Errorf("%s.SetPrec(%d).Prec() == %d; want %d", test.x, test.prec, got, prec)
}
if got, acc := x.String(), x.Acc(); got != test.want || acc != test.acc {
t.Errorf("%s.SetPrec(%d) = %s (%s); want %s (%s)", test.x, test.prec, got, acc, test.want, test.acc)
}
// look inside x and check correct value for x.exp
if len(x.mant) == 0 {
// ±0 or ±Inf
if x.exp != 0 && x.exp != infExp {
t.Errorf("%s.SetPrec(%d): incorrect exponent %d", test.x, test.prec, x.exp)
}
}
}
}
func TestFloatSign(t *testing.T) { func TestFloatSign(t *testing.T) {
for _, test := range []struct { for _, test := range []struct {
x string x string
......
...@@ -277,11 +277,11 @@ func (x *Float) bstring(buf []byte) []byte { ...@@ -277,11 +277,11 @@ func (x *Float) bstring(buf []byte) []byte {
// adjust mantissa to use exactly x.prec bits // adjust mantissa to use exactly x.prec bits
m := x.mant m := x.mant
switch w := uint(len(x.mant)) * _W; { switch w := uint32(len(x.mant)) * _W; {
case w < x.prec: case w < x.prec:
m = nat(nil).shl(m, x.prec-w) m = nat(nil).shl(m, uint(x.prec-w))
case w > x.prec: case w > x.prec:
m = nat(nil).shr(m, w-x.prec) m = nat(nil).shr(m, uint(w-x.prec))
} }
buf = append(buf, m.decimalString()...) buf = append(buf, m.decimalString()...)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment