internal/encoding/text: format using 32 bitsize when encoding float32

When encoding/textpb marshals out float32 values, it was previously
formatting it as float64 bitsize since both float types are stored as
float64 and internal/encoding/text only has one Float type.  A
consequence of this is that the output may display a different value
than expected, e.g.  1.02 becomes 1.0199999809265137.

This CL splits Float type into Float32 and Float64 to keep track of
which bitsize to use when formatting.  Values of both types are still
stored as float64 to keep the logic simple.

Decoding will always use Float64, but users can ask for a float32 value
from it.

Change-Id: Iea5b14b283fec2236a0c3946fac34d4d79b95274
Reviewed-on: https://go-review.googlesource.com/c/158497
Reviewed-by: Damien Neil <dneil@google.com>
diff --git a/internal/encoding/text/number.go b/internal/encoding/text/number.go
index 8a79f49..9c30a18 100644
--- a/internal/encoding/text/number.go
+++ b/internal/encoding/text/number.go
@@ -15,7 +15,7 @@
 	"github.com/golang/protobuf/v2/internal/errors"
 )
 
-// marshalNumber encodes v as either a Bool, Int, Uint, or Float.
+// marshalNumber encodes v as either a Bool, Int, Uint, Float32, or Float64.
 func (p *encoder) marshalNumber(v Value) error {
 	var err error
 	p.out, err = appendNumber(p.out, v)
@@ -24,7 +24,7 @@
 func appendNumber(out []byte, v Value) ([]byte, error) {
 	if len(v.raw) > 0 {
 		switch v.Type() {
-		case Bool, Int, Uint, Float:
+		case Bool, Int, Uint, Float32, Float64:
 			return append(out, v.raw...), nil
 		}
 	}
@@ -39,22 +39,28 @@
 		return strconv.AppendInt(out, int64(v.num), 10), nil
 	case Uint:
 		return strconv.AppendUint(out, uint64(v.num), 10), nil
-	case Float:
-		switch n := math.Float64frombits(v.num); {
-		case math.IsNaN(n):
-			return append(out, "nan"...), nil
-		case math.IsInf(n, +1):
-			return append(out, "inf"...), nil
-		case math.IsInf(n, -1):
-			return append(out, "-inf"...), nil
-		default:
-			return strconv.AppendFloat(out, n, 'g', -1, 64), nil
-		}
+	case Float32:
+		return appendFloat(out, v, 32)
+	case Float64:
+		return appendFloat(out, v, 64)
 	default:
 		return nil, errors.New("invalid type %v, expected bool or number", v.Type())
 	}
 }
 
+func appendFloat(out []byte, v Value, bitSize int) ([]byte, error) {
+	switch n := math.Float64frombits(v.num); {
+	case math.IsNaN(n):
+		return append(out, "nan"...), nil
+	case math.IsInf(n, +1):
+		return append(out, "inf"...), nil
+	case math.IsInf(n, -1):
+		return append(out, "-inf"...), nil
+	default:
+		return strconv.AppendFloat(out, n, 'g', -1, bitSize), nil
+	}
+}
+
 // These regular expressions were derived by reverse engineering the C++ code
 // in tokenizer.cc and text_format.cc.
 var (
@@ -80,7 +86,7 @@
 	floatRegexp   = regexp.MustCompile("^-?((0|[1-9][0-9]*)?([.][0-9]*)?([eE][+-]?[0-9]+)?[fF]?)")
 )
 
-// unmarshalNumber decodes a Bool, Int, Uint, or Float from the input.
+// unmarshalNumber decodes a Bool, Int, Uint, or Float64 from the input.
 func (p *decoder) unmarshalNumber() (Value, error) {
 	v, n, err := consumeNumber(p.in)
 	p.consume(n)
@@ -98,6 +104,7 @@
 	if n := matchWithDelim(floatRegexp, in); n > 0 {
 		if bytes.ContainsAny(in[:n], ".eEfF") {
 			s := strings.TrimRight(string(in[:n]), "fF")
+			// Always decode float as 64-bit.
 			f, err := strconv.ParseFloat(s, 64)
 			if err != nil {
 				return Value{}, 0, err