简体   繁体   English

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

[英]Unmarshal json to float. Why is float64 required?

I've noticed some strange behavior with the way go unmarshals json floats.我注意到 go 解组 json 浮动的方式有一些奇怪的行为。 Some numbers, but not all, refuse to unmarshal correctly.一些数字,但不是全部,拒绝正确解组。 Fixing this is as easy as using a float64 instead of a float32 in the destination variable, but for the life of me I can't find a good reason why this is the case.解决这个问题就像在目标变量中使用 float64 而不是 float32 一样简单,但是对于我的生活,我找不到一个很好的理由为什么会出现这种情况。

Here is code that demonstrates the problem:这是演示该问题的代码:

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
}

A float64 isn't required to accurately represent my example number, so why is required here?不需要 float64 来准确表示我的示例编号,那么这里为什么需要呢?

Go playground link: https://play.golang.org/p/tHkonQtZoCt Go 操场链接: https://play.golang.org/p/tHkonQtZoCt

Your assertion is wrong: 369.1368 cannot be represented exactly by either float32 or float64 .您的断言是错误369.1368不能用float32float64精确表示。

The closest float32 value is (approximately) 369.136810302734375 , whcih rounds to 369.13681 which is where your extra digit comes from.最接近的float32值是(大约) 369.136810302734375 ,四舍五入为369.13681 ,这是您的额外数字的来源。 The closest float64 value is (approximately) 369.13679999999999382 , which rounds more nicely for your purposes.最接近的float64值是(大约) 369.13679999999999382 ,对于您的目的而言,它会更好地舍入。

(Of course, if you round either of these to just four digits after the decimal point, you get the number you expected.) (当然,如果你将其中任何一个四舍五入到小数点后四位,你就会得到你期望的数字。)

A Decimal representation is exact: there is no rounding error. Decimal表示是精确的:没有舍入误差。

JSON transmits and receives floating point values expressed in decimal, but actual implementations , in various languages, then encode those numbers in different ways. JSON 传输和接收以十进制表示的浮点值,但在各种语言的实际实现中,然后以不同的方式对这些数字进行编码 Depending on what sort of entity you're talking to via JSON, encoding and decoding via Decimal could preserve the number exactly as you'd like, but be aware that programs written in, say, C++ or Python might decode your number to a different floating-point precision and introduce various rounding errors.根据您通过 JSON 与之交谈的实体类型,通过Decimal进行编码和解码可以完全按照您的意愿保留数字,但请注意,用 C++ 或 ZA7F5F35426B92741173Z 编写的程序可能会将您的数字解码为不同的 a5638浮点精度并引入各种舍入误差。

This Go Playground example uses the newly-added %x format, and shows you how the numbers are stored internally:这个 Go Playground 示例使用新添加的%x格式,并向您展示了数字是如何在内部存储的:

as float32 = 369.13681030273437500 (float32), which is really 12095875p-15 or 0x1.712306p+08作为 float32 = 369.13681030273437500 (float32),实际上是 12095875p-15 或 0x1.712306p+08

and:和:

as float64 = 369.13679999999999382 (float64), which is really 6493923261440380p-44 or 0x1.712305532617cp+08作为 float64 = 369.13679999999999382 (float64),实际上是 6493923261440380p-44 或 0x1.712305532617cp+08

That is, the number 369. whatever is internally represented in binary.也就是说,数字 369。无论在内部以二进制表示。 It's between 2 8 = 256 and 2 9 = 512. In binary, it is 1 256, no 128, 1 64, 1 32, 1 16, no 8, no 4, no 2, and 1 1: 1.01110001 something x 2 8 .它在 2 8 = 256 和 2 9 = 512 之间。在二进制中,它是 1 256、没有 128、1 64、1 32、1 16、没有 8、没有 4、没有 2 和 1 1:1.01110001 something x 2 8 . The %b format expresses this one way and the %x format another, with %x starting with 1.72 (1. 0111 0010). %b格式表示这种方式, %x格式表示另一种方式, %x1.72 (1. 0111 0010) 开头。

See Is floating point math broken?请参阅浮点数学是否损坏? (as jub0bs linked in a comment) for more. (作为评论中链接的 jub0bs )了解更多信息。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM