Commit 79c12958 authored by Robert Griesemer's avatar Robert Griesemer

math/big: export Float.MinPrec

MinPrec returns the minimum precision required to represent a Float
without loss of precision. Added test.

Change-Id: I466c8e492dcdd59fae854fc4e71ef9b1add7d817
Reviewed-on: https://go-review.googlesource.com/6010Reviewed-by: default avatarAlan Donovan <adonovan@google.com>
parent e4791066
...@@ -196,6 +196,13 @@ func (x *Float) Prec() uint { ...@@ -196,6 +196,13 @@ func (x *Float) Prec() uint {
return uint(x.prec) return uint(x.prec)
} }
// MinPrec returns the minimum precision required to represent x exactly
// (i.e., the smallest prec before x.SetPrec(prec) would start rounding x).
// The result is 0 for ±0 and ±Inf.
func (x *Float) MinPrec() uint {
return uint(len(x.mant))*_W - x.mant.trailingZeroBits()
}
// Acc returns the accuracy of x produced by the most recent operation. // Acc returns the accuracy of x produced by the most recent operation.
func (x *Float) Acc() Accuracy { func (x *Float) Acc() Accuracy {
return x.acc return x.acc
...@@ -281,7 +288,7 @@ func (x *Float) IsInt() bool { ...@@ -281,7 +288,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 <= uint32(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.
...@@ -680,13 +687,6 @@ func high64(x nat) uint64 { ...@@ -680,13 +687,6 @@ func high64(x nat) uint64 {
return v return v
} }
// minPrec returns the minimum precision required to represent
// x without loss of accuracy.
// TODO(gri) this might be useful to export, perhaps under a better name
func (x *Float) minPrec() uint {
return uint(len(x.mant))*_W - x.mant.trailingZeroBits()
}
// Uint64 returns the unsigned integer resulting from truncating x // Uint64 returns the unsigned integer resulting from truncating x
// towards zero. If 0 <= x <= math.MaxUint64, the result is Exact // towards zero. If 0 <= x <= math.MaxUint64, the result is Exact
// if x is an integer and Below otherwise. // if x is an integer and Below otherwise.
...@@ -713,7 +713,7 @@ func (x *Float) Uint64() (uint64, Accuracy) { ...@@ -713,7 +713,7 @@ func (x *Float) Uint64() (uint64, Accuracy) {
if x.exp <= 64 { if x.exp <= 64 {
// u = trunc(x) fits into a uint64 // u = trunc(x) fits into a uint64
u := high64(x.mant) >> (64 - uint32(x.exp)) u := high64(x.mant) >> (64 - uint32(x.exp))
if x.minPrec() <= 64 { if x.MinPrec() <= 64 {
return u, Exact return u, Exact
} }
return u, Below // x truncated return u, Below // x truncated
...@@ -760,14 +760,14 @@ func (x *Float) Int64() (int64, Accuracy) { ...@@ -760,14 +760,14 @@ func (x *Float) Int64() (int64, Accuracy) {
if x.neg { if x.neg {
i = -i i = -i
} }
if x.minPrec() <= 63 { if x.MinPrec() <= 63 {
return i, Exact return i, Exact
} }
return i, acc // x truncated return i, acc // x truncated
} }
if x.neg { if x.neg {
// check for special case x == math.MinInt64 (i.e., x == -(0.5 << 64)) // check for special case x == math.MinInt64 (i.e., x == -(0.5 << 64))
if x.exp == 64 && x.minPrec() == 1 { if x.exp == 64 && x.MinPrec() == 1 {
acc = Exact acc = Exact
} }
return math.MinInt64, acc return math.MinInt64, acc
...@@ -844,7 +844,7 @@ func (x *Float) Int(z *Int) (*Int, Accuracy) { ...@@ -844,7 +844,7 @@ func (x *Float) Int(z *Int) (*Int, Accuracy) {
// determine minimum required precision for x // determine minimum required precision for x
allBits := uint(len(x.mant)) * _W allBits := uint(len(x.mant)) * _W
exp := uint(x.exp) exp := uint(x.exp)
if x.minPrec() <= exp { if x.MinPrec() <= exp {
acc = Exact acc = Exact
} }
// shift mantissa as needed // shift mantissa as needed
......
...@@ -152,6 +152,31 @@ func TestFloatSetPrec(t *testing.T) { ...@@ -152,6 +152,31 @@ func TestFloatSetPrec(t *testing.T) {
} }
} }
func TestFloatMinPrec(t *testing.T) {
const max = 100
for _, test := range []struct {
x string
want uint
}{
{"0", 0},
{"-0", 0},
{"+Inf", 0},
{"-Inf", 0},
{"1", 1},
{"2", 1},
{"3", 2},
{"0x8001", 16},
{"0x8001p-1000", 16},
{"0x8001p+1000", 16},
{"0.1", max},
} {
x := makeFloat(test.x).SetPrec(max)
if got := x.MinPrec(); got != test.want {
t.Errorf("%s.MinPrec() = %d; want %d", test.x, got, test.want)
}
}
}
func TestFloatSign(t *testing.T) { func TestFloatSign(t *testing.T) {
for _, test := range []struct { for _, test := range []struct {
x string x string
......
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