goprotobuf: Write C++-compatible ±Inf and NaN floats in text format.
R=r
CC=golang-dev
https://codereview.appspot.com/10113046
diff --git a/proto/testdata/test.proto b/proto/testdata/test.proto
index 890efde..73e5436 100644
--- a/proto/testdata/test.proto
+++ b/proto/testdata/test.proto
@@ -351,3 +351,7 @@
optional int32 y = 3;
}
}
+
+message FloatingPoint {
+ required double f = 1;
+}
diff --git a/proto/text.go b/proto/text.go
index 8c39b8a..ff8a0bb 100644
--- a/proto/text.go
+++ b/proto/text.go
@@ -39,6 +39,7 @@
"fmt"
"io"
"log"
+ "math"
"os"
"reflect"
"sort"
@@ -55,6 +56,9 @@
backslashT = []byte{'\\', 't'}
backslashDQ = []byte{'\\', '"'}
backslashBS = []byte{'\\', '\\'}
+ posInf = []byte("inf")
+ negInf = []byte("-inf")
+ nan = []byte("nan")
)
type writer interface {
@@ -292,8 +296,27 @@
func writeAny(w *textWriter, v reflect.Value, props *Properties) error {
v = reflect.Indirect(v)
+ // Floats have special cases.
+ if v.Kind() == reflect.Float32 || v.Kind() == reflect.Float64 {
+ x := v.Float()
+ var b []byte
+ switch {
+ case math.IsInf(x, 1):
+ b = posInf
+ case math.IsInf(x, -1):
+ b = negInf
+ case math.IsNaN(x):
+ b = nan
+ }
+ if b != nil {
+ _, err := w.Write(b)
+ return err
+ }
+ // Other values are handled below.
+ }
+
// We don't attempt to serialise every possible value type; only those
- // that can occur in protocol buffers, plus a few extra that were easy.
+ // that can occur in protocol buffers.
switch v.Kind() {
case reflect.Slice:
// Should only be a []byte; repeated fields are handled in writeStruct.
diff --git a/proto/text_test.go b/proto/text_test.go
index e3bb791..f5d0574 100644
--- a/proto/text_test.go
+++ b/proto/text_test.go
@@ -35,6 +35,7 @@
"bytes"
"errors"
"io/ioutil"
+ "math"
"strings"
"testing"
@@ -333,3 +334,24 @@
}
}
}
+
+func TestFloats(t *testing.T) {
+ tests := []struct {
+ f float64
+ want string
+ }{
+ {0, "0"},
+ {4.7, "4.7"},
+ {math.Inf(1), "inf"},
+ {math.Inf(-1), "-inf"},
+ {math.NaN(), "nan"},
+ }
+ for _, test := range tests {
+ msg := &pb.FloatingPoint{F: &test.f}
+ got := strings.TrimSpace(msg.String())
+ want := `f:` + test.want
+ if got != want {
+ t.Errorf("f=%f: got %q, want %q", test.f, got, want)
+ }
+ }
+}