解组 json 以浮动。为什么需要 float64?

Unmarshal json to float. Why is float64 required?

我注意到 go unmarshals json 浮点数的一些奇怪行为。有些数字(但不是全部)拒绝正确解组。解决这个问题就像在目标变量中使用 float64 而不是 float32 一样简单,但对于我来说,我找不到一个很好的理由来解释为什么会这样。

这是演示问题的代码:

package main

import (
    "encoding/json"
    "fmt"
    . "github.com/shopspring/decimal"
)

func main() {
    bytes, _ := json.Marshal(369.1368) // not every number is broken, but this one is
    fmt.Println("bytes", string(bytes))

    var f32 float32
    json.Unmarshal(bytes, &f32)
    fmt.Printf("f32 %f\n", f32) // adds an extra 0.00001 to the number

    var d Decimal
    json.Unmarshal(bytes, &d)
    fmt.Printf("d %s\n", d) // 3rd party packages work

    // naw, you can just float64
    var f64 float64
    json.Unmarshal(bytes, &f64)
    fmt.Printf("f64 %f\n", f64) // float64 works
}

不需要 float64 来准确表示我的示例数字,那么这里为什么需要 float64?

去游乐场link:https://play.golang.org/p/tHkonQtZoCt

您的断言是错误的:369.1368 不能准确地表示为 或者 float32 float64.

最接近的 float32 值是(大约)369.136810302734375,四舍五入到 369.13681,这是您额外数字的来源。最接近的 float64 值是(大约)369.13679999999999382,它更适合您的目的。

(当然,如果你将其中任何一个四舍五入到小数点后四位,你就会得到你期望的数字。)

Decimal表示是准确的:没有舍入误差。

JSON 传输和接收以十进制表示的浮点值,但实际 实现 ,在各种语言中,然后 编码 这些数字以不同的方式。根据您通过 JSON 与哪种实体交谈,通过 Decimal 进行编码和解码可以完全按照您的意愿保留数字,但请注意,用 C++ 或 C++ 编写的程序Python 可能会将您的数字解码为不同的浮点精度并引入各种舍入误差。

This Go Playground example 使用新添加的 %x 格式,并向您展示数字在内部的存储方式:

as float32 = 369.13681030273437500 (float32), which is really 12095875p-15 or 0x1.712306p+08

和:

as float64 = 369.13679999999999382 (float64), which is really 6493923261440380p-44 or 0x1.712305532617cp+08

即数字 369.随便什么 在内部用二进制表示。介于28 = 256和29 = 512之间,二进制是1 256,没有128, 1 64, 1 32, 1 16、8 号、4 号、2 号和 1 1:1.01110001某物 x 28%b 格式表示这种方式,%x 格式表示另一种方式,%x1.72 (1 . 0111 0010).

开头

有关更多信息,请参阅 Is floating point math broken?(如评论中链接的 jub0bs)。