Commit 363617c7 authored by Robert Griesemer's avatar Robert Griesemer

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/6870Reviewed-by: default avatarAlan Donovan <adonovan@google.com>
parent 0ff7c3ea
...@@ -37,6 +37,9 @@ const maxShift = _W - 4 ...@@ -37,6 +37,9 @@ const maxShift = _W - 4
// precision argument and keeping track of when a number was truncated early // precision argument and keeping track of when a number was truncated early
// (equivalent of "sticky bit" in binary rounding). // (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 // Init initializes x to the decimal representation of m << shift (for
// shift >= 0), or m >> -shift (for shift < 0). // shift >= 0), or m >> -shift (for shift < 0).
func (x *decimal) init(m nat, shift int) { func (x *decimal) init(m nat, shift int) {
......
This diff is collapsed.
...@@ -90,15 +90,20 @@ func TestFloatZeroValue(t *testing.T) { ...@@ -90,15 +90,20 @@ func TestFloatZeroValue(t *testing.T) {
func makeFloat(s string) *Float { func makeFloat(s string) *Float {
var x 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) return x.SetInf(+1)
} case "-Inf":
if s == "-Inf" {
return x.SetInf(-1) return x.SetInf(-1)
} case "NaN", "-NaN":
if s == "NaN" || s == "-NaN" {
return x.SetNaN() return x.SetNaN()
} }
x.SetPrec(1000) x.SetPrec(1000)
if _, ok := x.SetString(s); !ok { if _, ok := x.SetString(s); !ok {
panic(fmt.Sprintf("%q is not a valid float", s)) panic(fmt.Sprintf("%q is not a valid float", s))
...@@ -146,13 +151,6 @@ func TestFloatSetPrec(t *testing.T) { ...@@ -146,13 +151,6 @@ func TestFloatSetPrec(t *testing.T) {
if got, acc := x.String(), x.Acc(); got != test.want || acc != test.acc { 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) 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 { ...@@ -209,7 +207,7 @@ func feq(x, y *Float) bool {
if x.IsNaN() || y.IsNaN() { if x.IsNaN() || y.IsNaN() {
return 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) { func TestFloatMantExp(t *testing.T) {
...@@ -261,9 +259,9 @@ func TestFloatSetMantExp(t *testing.T) { ...@@ -261,9 +259,9 @@ func TestFloatSetMantExp(t *testing.T) {
{"Inf", 1234, "+Inf"}, {"Inf", 1234, "+Inf"},
{"+Inf", -1234, "+Inf"}, {"+Inf", -1234, "+Inf"},
{"-Inf", -1234, "-Inf"}, {"-Inf", -1234, "-Inf"},
{"0", -MaxExp - 1, "0"}, {"0", MinExp, "0"},
{"0.5", -MaxExp - 1, "+0"}, // exponent underflow {"0.25", MinExp, "+0"}, // exponent underflow
{"-0.5", -MaxExp - 1, "-0"}, // exponent underflow {"-0.25", MinExp, "-0"}, // exponent underflow
{"1", MaxExp, "+Inf"}, // exponent overflow {"1", MaxExp, "+Inf"}, // exponent overflow
{"2", MaxExp - 1, "+Inf"}, // exponent overflow {"2", MaxExp - 1, "+Inf"}, // exponent overflow
{"0.75", 1, "1.5"}, {"0.75", 1, "1.5"},
......
...@@ -96,11 +96,13 @@ func (z *Float) Scan(r io.ByteScanner, base int) (f *Float, b int, err error) { ...@@ -96,11 +96,13 @@ func (z *Float) Scan(r io.ByteScanner, base int) (f *Float, b int, err error) {
// special-case 0 // special-case 0
if len(z.mant) == 0 { if len(z.mant) == 0 {
z.acc = Exact z.acc = Exact
z.exp = 0 z.form = zero
return return
} }
// len(z.mant) > 0 // len(z.mant) > 0
z.form = finite
// The mantissa may have a decimal point (fcount <= 0) and there // The mantissa may have a decimal point (fcount <= 0) and there
// may be a nonzero exponent exp. The decimal point amounts to a // may be a nonzero exponent exp. The decimal point amounts to a
// division by b**(-fcount). An exponent means multiplication by // division by b**(-fcount). An exponent means multiplication by
...@@ -275,9 +277,13 @@ func (x *Float) bstring(buf []byte) []byte { ...@@ -275,9 +277,13 @@ func (x *Float) bstring(buf []byte) []byte {
if x.neg { if x.neg {
buf = append(buf, '-') buf = append(buf, '-')
} }
if len(x.mant) == 0 { if x.form == zero {
return append(buf, '0') return append(buf, '0')
} }
if debugFloat && x.form != finite {
panic("non-finite float")
}
// x != 0 // x != 0
// adjust mantissa to use exactly x.prec bits // adjust mantissa to use exactly x.prec bits
...@@ -306,9 +312,13 @@ func (x *Float) pstring(buf []byte) []byte { ...@@ -306,9 +312,13 @@ func (x *Float) pstring(buf []byte) []byte {
if x.neg { if x.neg {
buf = append(buf, '-') buf = append(buf, '-')
} }
if len(x.mant) == 0 { if x.form == zero {
return append(buf, '0') return append(buf, '0')
} }
if debugFloat && x.form != finite {
panic("non-finite float")
}
// x != 0 // x != 0
// remove trailing 0 words early // remove trailing 0 words early
......
...@@ -19,11 +19,17 @@ import "strconv" ...@@ -19,11 +19,17 @@ import "strconv"
// bigFtoa formats a float for the %e, %E, %f, %g, and %G formats. // bigFtoa formats a float for the %e, %E, %f, %g, and %G formats.
func (f *Float) bigFtoa(buf []byte, fmt byte, prec int) []byte { 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 // 1) convert Float to multiprecision decimal
var mant nat
if f.form == finite {
mant = f.mant
}
var d decimal 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 // 2) round to desired precision
shortest := false shortest := false
......
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