Skip to content

encoding/json/jsontext: support 32-bit floating-point numbers #76430

@ChrisHines

Description

@ChrisHines

Go version

go1.25.x

Output of go env in your module/workspace:

GOEXPERIMENT=jsonv2

What did you do?

func main() {
	var (
		pi32 = float32(math.Pi)
		pi64 = float64(math.Pi)
	)

	m := map[string]any{
		"f32": pi32,
		"f64": pi64,
	}

	out, _ := json.Marshal(m, jsontext.WithIndent("    "))
	fmt.Printf("%s\n", out)

	enc := jsontext.NewEncoder(os.Stdout, jsontext.WithIndent("    "))
	enc.WriteToken(jsontext.BeginObject)

	enc.WriteToken(jsontext.String("f32"))
	enc.WriteToken(jsontext.Float(float64(pi32)))

	enc.WriteToken(jsontext.String("f64"))
	enc.WriteToken(jsontext.Float(pi64))

	enc.WriteToken(jsontext.EndObject)
}

What did you see happen?

The above program prints:

{
    "f32": 3.1415927,
    "f64": 3.141592653589793
}
{
    "f32": 3.1415927410125732,
    "f64": 3.141592653589793
}

Notice how json.Marshal nicely formats the float32 version of pi with a correct number of significant digits while the most obvious way to produce the same output with jsontext.Encoder formats it with a misleading (and wrong) set of digits.

I discovered this while experimenting with retrofitting the json implementation of a structured logging encoder to use jsontext. Although the logging encoder in question is not public its encoding interface is quite similar to the zapcore.ObjectEncoder which has an AddFloat32(key string, value float32) method.

Our log encoder does not use the json package but does have unit tests that ensure it produces the same output as an equivalent json.Marshal call for all the primitive types we support. Using jsontext.Encoder it was trivial to match the output for all types except float32.

Note that in order to match the json.Marshal behavior for floating point values our current log encoder has a copy of an older version of the code that json/v2 has in the encoding/json/internal/jsonwire.AppendFloat function.

What did you expect to see?

I think the jsontext package needs better support for float32 tokens.

For use cases like the above there should be a way to construct a jsontext.Token that carries a float32 value in a way that the Encoder eventually calls jsonwire.AppendFloat with 32 for its bits parameter.

Metadata

Metadata

Assignees

No one assigned

    Labels

    LibraryProposalIssues describing a requested change to the Go standard library or x/ libraries, but not to a toolNeedsInvestigationSomeone must examine and confirm this is a valid issue and not a duplicate of an existing one.

    Type

    No type

    Projects

    Status

    Implementing

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions