math/big: fix latent decimal conversion bug

A decimal represented 0.0 with a 0-length mantissa and undefined
exponent, but the formatting code assumes a valid zero exponent
if the float value is 0.0. The code worked because we allocate a
new decimal value each time and because there's no rounding that
lead to 0.0.

Change-Id: Ifd771d7709de83b87fdbf141786286b4c3e13d4f
Reviewed-on: https://go-review.googlesource.com/10448
Reviewed-by: Alan Donovan <adonovan@google.com>
diff --git a/src/math/big/decimal.go b/src/math/big/decimal.go
index 3d024dc..2595e5f 100644
--- a/src/math/big/decimal.go
+++ b/src/math/big/decimal.go
@@ -19,12 +19,14 @@
 
 package big
 
-// A decimal represents a floating-point number in decimal representation.
-// The value of a decimal x is x.mant * 10 ** x.exp with 0.5 <= x.mant < 1,
-// with the most-significant mantissa digit at index 0.
+// A decimal represents an unsigned floating-point number in decimal representation.
+// The value of a non-zero decimal x is x.mant * 10 ** x.exp with 0.5 <= x.mant < 1,
+// with the most-significant mantissa digit at index 0. For the zero decimal, the
+// mantissa length and exponent are 0.
+// The zero value for decimal represents a ready-to-use 0.0.
 type decimal struct {
 	mant []byte // mantissa ASCII digits, big-endian
-	exp  int    // exponent, valid if len(mant) > 0
+	exp  int    // exponent
 }
 
 // Maximum shift amount that can be done in one pass without overflow.
@@ -46,6 +48,7 @@
 	// special case 0
 	if len(m) == 0 {
 		x.mant = x.mant[:0]
+		x.exp = 0
 		return
 	}
 
@@ -255,4 +258,7 @@
 		i--
 	}
 	x.mant = x.mant[:i]
+	if i == 0 {
+		x.exp = 0
+	}
 }
diff --git a/src/math/big/floatconv_test.go b/src/math/big/floatconv_test.go
index db30031..9fc2b89 100644
--- a/src/math/big/floatconv_test.go
+++ b/src/math/big/floatconv_test.go
@@ -125,12 +125,16 @@
 		{1, 'f', 0, "1"},
 		{-1, 'f', 0, "-1"},
 
+		{0.001, 'e', 0, "1e-03"},
+		{0.459, 'e', 0, "5e-01"},
 		{1.459, 'e', 0, "1e+00"},
 		{2.459, 'e', 1, "2.5e+00"},
 		{3.459, 'e', 2, "3.46e+00"},
 		{4.459, 'e', 3, "4.459e+00"},
 		{5.459, 'e', 4, "5.4590e+00"},
 
+		{0.001, 'f', 0, "0"},
+		{0.459, 'f', 0, "0"},
 		{1.459, 'f', 0, "1"},
 		{2.459, 'f', 1, "2.5"},
 		{3.459, 'f', 2, "3.46"},
diff --git a/src/math/big/ftoa.go b/src/math/big/ftoa.go
index 0f943e1..4c3e743 100644
--- a/src/math/big/ftoa.go
+++ b/src/math/big/ftoa.go
@@ -58,7 +58,7 @@
 	}
 
 	// Inf
-	if x.IsInf() {
+	if x.form == inf {
 		if !x.neg {
 			buf = append(buf, '+')
 		}
@@ -79,12 +79,10 @@
 	//   3) read digits out and format
 
 	// 1) convert Float to multiprecision decimal
-	var mant nat
+	var d decimal // == 0.0
 	if x.form == finite {
-		mant = x.mant
+		d.init(x.mant, int(x.exp)-x.mant.bitLen())
 	}
-	var d decimal
-	d.init(mant, int(x.exp)-x.mant.bitLen())
 
 	// 2) round to desired precision
 	shortest := false