math/big: added (internal) Float.form field for easier case distinctions

This is a fairly significant _internal_ representation change. Instead
of encoding 0, finite, infinite, and NaN values with special mantissa
and exponent values, a new (1 byte) 'form' field is used (without making
the Float struct bigger). The form field permits simpler and faster
case distinctions. As a side benefit, for zero and non-finite floats,
fewer fields need to be set. Also, the exponent range is not the full
int32 range (in the old format, infExp and nanExp were used to represent
Inf and NaN values and tests for those values sometimes didn't test
for the empty mantissa, so the range was reduced by 2 values).

The correspondence between the old and new fields is as follows.
Old representation:

x                 neg      mant         exp
---------------------------------------------------------------
+/-0              sign     empty        0
0 < |x| < +Inf    sign     mantissa     exponent
+/-Inf            sign     empty        infExp
NaN               false    empty        nanExp

New representation (- stands for ignored fields):

x                 neg      mant         exp         form
---------------------------------------------------------------
+/-0              sign     -            -           zero
0 < |x| < +Inf    sign     mantissa     exponent    finite
+/-Inf            sign     -            -           inf
NaN               -        -            -           nan

Client should not be affected by this change.

Change-Id: I7e355894d602ceb23f9ec01da755fe6e0386b101
Reviewed-on: https://go-review.googlesource.com/6870
Reviewed-by: Alan Donovan <adonovan@google.com>
This commit is contained in:
Robert Griesemer 2015-03-04 17:00:41 -08:00
parent 0ff7c3ea45
commit 363617c7d3
5 changed files with 375 additions and 341 deletions

View file

@ -37,6 +37,9 @@ const maxShift = _W - 4
// precision argument and keeping track of when a number was truncated early
// (equivalent of "sticky bit" in binary rounding).
// TODO(gri) Along the same lines, enforce some limit to shift magnitudes
// to avoid "infinitely" long running conversions (until we run out of space).
// Init initializes x to the decimal representation of m << shift (for
// shift >= 0), or m >> -shift (for shift < 0).
func (x *decimal) init(m nat, shift int) {

File diff suppressed because it is too large Load diff

View file

@ -90,15 +90,20 @@ func TestFloatZeroValue(t *testing.T) {
func makeFloat(s string) *Float {
var x Float
if s == "Inf" || s == "+Inf" {
switch s {
case "0":
return &x
case "-0":
return x.Neg(&x)
case "Inf", "+Inf":
return x.SetInf(+1)
}
if s == "-Inf" {
case "-Inf":
return x.SetInf(-1)
}
if s == "NaN" || s == "-NaN" {
case "NaN", "-NaN":
return x.SetNaN()
}
x.SetPrec(1000)
if _, ok := x.SetString(s); !ok {
panic(fmt.Sprintf("%q is not a valid float", s))
@ -146,13 +151,6 @@ func TestFloatSetPrec(t *testing.T) {
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, ±Inf, or NaN
if x.exp != 0 && x.exp > MinExp {
t.Errorf("%s.SetPrec(%d): incorrect exponent %d", test.x, test.prec, x.exp)
}
}
}
}
@ -209,7 +207,7 @@ func feq(x, y *Float) bool {
if x.IsNaN() || y.IsNaN() {
return x.IsNaN() && y.IsNaN()
}
return x.Cmp(y) == 0 && x.neg == y.neg
return x.Cmp(y) == 0 && x.IsNeg() == y.IsNeg()
}
func TestFloatMantExp(t *testing.T) {
@ -261,11 +259,11 @@ func TestFloatSetMantExp(t *testing.T) {
{"Inf", 1234, "+Inf"},
{"+Inf", -1234, "+Inf"},
{"-Inf", -1234, "-Inf"},
{"0", -MaxExp - 1, "0"},
{"0.5", -MaxExp - 1, "+0"}, // exponent underflow
{"-0.5", -MaxExp - 1, "-0"}, // exponent underflow
{"1", MaxExp, "+Inf"}, // exponent overflow
{"2", MaxExp - 1, "+Inf"}, // exponent overflow
{"0", MinExp, "0"},
{"0.25", MinExp, "+0"}, // exponent underflow
{"-0.25", MinExp, "-0"}, // exponent underflow
{"1", MaxExp, "+Inf"}, // exponent overflow
{"2", MaxExp - 1, "+Inf"}, // exponent overflow
{"0.75", 1, "1.5"},
{"0.5", 11, "1024"},
{"-0.5", -2, "-0.125"},

View file

@ -96,11 +96,13 @@ func (z *Float) Scan(r io.ByteScanner, base int) (f *Float, b int, err error) {
// special-case 0
if len(z.mant) == 0 {
z.acc = Exact
z.exp = 0
z.form = zero
return
}
// len(z.mant) > 0
z.form = finite
// The mantissa may have a decimal point (fcount <= 0) and there
// may be a nonzero exponent exp. The decimal point amounts to a
// division by b**(-fcount). An exponent means multiplication by
@ -275,9 +277,13 @@ func (x *Float) bstring(buf []byte) []byte {
if x.neg {
buf = append(buf, '-')
}
if len(x.mant) == 0 {
if x.form == zero {
return append(buf, '0')
}
if debugFloat && x.form != finite {
panic("non-finite float")
}
// x != 0
// adjust mantissa to use exactly x.prec bits
@ -306,9 +312,13 @@ func (x *Float) pstring(buf []byte) []byte {
if x.neg {
buf = append(buf, '-')
}
if len(x.mant) == 0 {
if x.form == zero {
return append(buf, '0')
}
if debugFloat && x.form != finite {
panic("non-finite float")
}
// x != 0
// remove trailing 0 words early

View file

@ -19,11 +19,17 @@ import "strconv"
// bigFtoa formats a float for the %e, %E, %f, %g, and %G formats.
func (f *Float) bigFtoa(buf []byte, fmt byte, prec int) []byte {
// TODO(gri) handle Inf.
if debugFloat && !f.IsFinite() {
panic("non-finite float")
}
// 1) convert Float to multiprecision decimal
var mant nat
if f.form == finite {
mant = f.mant
}
var d decimal
d.init(f.mant, int(f.exp)-f.mant.bitLen())
d.init(mant, int(f.exp)-f.mant.bitLen())
// 2) round to desired precision
shortest := false