Skip to content

Commit

Permalink
Allow setting floating point precision in JSON.
Browse files Browse the repository at this point in the history
Fixes #557.
  • Loading branch information
mitar committed Apr 5, 2024
1 parent 2d899f0 commit f10673b
Show file tree
Hide file tree
Showing 4 changed files with 24 additions and 8 deletions.
3 changes: 3 additions & 0 deletions encoder_json.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ func init() {
json.JSONMarshalFunc = func(v interface{}) ([]byte, error) {
return InterfaceMarshalFunc(v)
}
json.FloatingPointPrecisionFunc = func() int {
return FloatingPointPrecision
}
}

func appendJSON(dst []byte, j []byte) []byte {
Expand Down
5 changes: 5 additions & 0 deletions globals.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,11 @@ var (
// TriggerLevelWriterBufferReuseLimit is a limit in bytes that a buffer is dropped
// from the TriggerLevelWriter buffer pool if the buffer grows above the limit.
TriggerLevelWriterBufferReuseLimit = 64 * 1024

// FloatingPointPrecision, if set to a value other than -1, controls the number
// of digits when formatting float numbers in JSON. See strconv.FormatFloat for
// more details.
FloatingPointPrecision = -1
)

var (
Expand Down
10 changes: 7 additions & 3 deletions internal/json/base.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
package json

// JSONMarshalFunc is used to marshal interface to JSON encoded byte slice.
// Making it package level instead of embedded in Encoder brings
// Making the following variables package level instead of embedded in Encoder brings
// some extra efforts at importing, but avoids value copy when the functions
// of Encoder being invoked.
// DO REMEMBER to set this variable at importing, or
// DO REMEMBER to set these variables at importing, or
// you might get a nil pointer dereference panic at runtime.

// JSONMarshalFunc is used to marshal interface to JSON encoded byte slice.
var JSONMarshalFunc func(v interface{}) ([]byte, error)

// FloatingPointPrecisionFunc is used to obtain the floating point formatting precision.
var FloatingPointPrecisionFunc func() int = func() int { return -1 }

type Encoder struct{}

// AppendKey appends a new key to the output JSON.
Expand Down
14 changes: 9 additions & 5 deletions internal/json/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -311,16 +311,20 @@ func appendFloat(dst []byte, val float64, bitSize int) []byte {
case math.IsInf(val, -1):
return append(dst, `"-Inf"`...)
}
precision := FloatingPointPrecisionFunc()
// convert as if by es6 number to string conversion
// see also https://cs.opensource.google/go/go/+/refs/tags/go1.20.3:src/encoding/json/encode.go;l=573
strFmt := byte('f')
// Use float32 comparisons for underlying float32 value to get precise cutoffs right.
if abs := math.Abs(val); abs != 0 {
if bitSize == 64 && (abs < 1e-6 || abs >= 1e21) || bitSize == 32 && (float32(abs) < 1e-6 || float32(abs) >= 1e21) {
strFmt = 'e'
// If precision is set to a value other than -1, we always just format the float using that precision.
if precision == -1 {
// Use float32 comparisons for underlying float32 value to get precise cutoffs right.
if abs := math.Abs(val); abs != 0 {
if bitSize == 64 && (abs < 1e-6 || abs >= 1e21) || bitSize == 32 && (float32(abs) < 1e-6 || float32(abs) >= 1e21) {
strFmt = 'e'
}
}
}
dst = strconv.AppendFloat(dst, val, strFmt, -1, bitSize)
dst = strconv.AppendFloat(dst, val, strFmt, precision, bitSize)
if strFmt == 'e' {
// Clean up e-09 to e-9
n := len(dst)
Expand Down

0 comments on commit f10673b

Please sign in to comment.