Repository: google/starlark-go (go)

Issue:
JSON encoding of floats does not match the documentation
Hello,

The [documentation](https://pkg.go.dev/go.starlark.net/lib/json) states that:

> Starlark float values are encoded using decimal point notation, even if the value is an integer.

This suggests that float(1) should be encoded as 1.0, similar to Python:

```python
>>> import json
>>> json.dumps([1.0])
"[1.0]"
```

However, Starlark uses `%g` formatting, which truncates trailing zeros and the decimal point. Hence:

```starlark
>>> json.encode([1.0])
"[1]"
```
It appears that "encoding/json" module in Go [exhibits the same behavior](https://stackoverflow.com/questions/20099328/does-go-correctly-marshal-float64-to-json).

Even a comment in the Starlark source code misleadingly states that a float representation "always contains a decimal point":
https://github.com/google/starlark-go/blob/1207426daebd7ea8eb86b8d917a344a72cc5167b/lib/json/json.go#L150

In my opinion, since Starlark tries to be similar to Python, the JSON serialization should behave the same way and preserve the information about the floating-point type. I don't expect that changing the formatting will break existing code, but even so the current implementation will definitely lead to bugs. However, in case you decide not to change anything here, please update the documentation accordingly.

Thank you!

Pull request description:
lib/json: encode floating point numbers with a decimal point
The previous code carelessly used Go's %g operator when it should have used Starlark's.

Fixes #563

Patch:
diff --git a/lib/json/json.go b/lib/json/json.go
index 5979b6f..bc67be5 100644
--- a/lib/json/json.go
+++ b/lib/json/json.go
@@ -147,7 +147,8 @@ func encode(thread *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, k
 			if !isFinite(float64(x)) {
 				return fmt.Errorf("cannot encode non-finite float %v", x)
 			}
-			fmt.Fprintf(buf, "%g", x) // always contains a decimal point
+			// Float.String always contains a decimal point. (%g does not!)
+			buf.WriteString(x.String())
 
 		case starlark.String:
 			quote(string(x))


Test patch:
diff --git a/starlark/testdata/json.star b/starlark/testdata/json.star
index 62a457b..1df9dee 100644
--- a/starlark/testdata/json.star
+++ b/starlark/testdata/json.star
@@ -144,8 +144,13 @@ numbers = [
     0, 1, -1, +1, 1.23e45, -1.23e-45,
     3539537889086624823140625,
     float(3539537889086624823140625),
+    float(1.0),
 ]
-assert.eq(codec(numbers), numbers)
+def number_roundtrip():
+    show = lambda n: (n, type(n))
+    for n in numbers:
+      assert.eq(show(codec(n)), show(n)) # ensure no int/float confusion
+number_roundtrip()
 
 ## json.indent