我注意到去解组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即可准确表示我的示例编号,所以为什么在这里需要?
前往游乐场链接:https://play.golang.org/p/tHkonQtZoCt
最佳答案
您的断言是错误的:369.1368
不能完全由float32
或float64
表示。
最接近的float32
值是(大约)369.136810302734375
,四舍五入到369.13681
,这是您的额外数字的来源。最接近的float64
值是(大约)369.13679999999999382
,为了您的目的,它的舍入效果更好。
(当然,如果您将其中任意一个四舍五入到小数点后的四位数,您就会得到预期的数字。)Decimal
表示形式很准确:没有舍入错误。
JSON发送和接收以十进制表示的浮点值,但实际的实现是使用各种语言,然后以不同的方式对这些数字进行编码。根据您通过JSON与哪种类型的实体进行对话,通过Decimal
进行编码和解码可以完全保留所需的数字,但是请注意,用C++或Python编写的程序可能会将您的数字解码为其他形式浮点精度,并引入各种舍入误差。
This Go Playground example使用新添加的%x
格式,并向您显示数字如何在内部存储:
如float32 = 369.13681030273437500(float32),实际上是12095875p-15或0x1.712306p + 08
和:
作为float64 = 369.13679999999999382(float64),实际上是6493923261440380p-44或0x1.712305532617cp + 08
也就是说,数字369.what内部以二进制表示。它在28 = 256和29 = 512之间。在二进制中,它是1 256,no 128、1 64、1 32、1 16,no 8,no 4,no 2和1 1:1.01110001某物x28。%b
format以一种方式表示,%x
以另一种方式表示,%x
以1.72
(1.0111 0010)开头。
有关更多信息,请参见Is floating point math broken?(作为jub0bs在评论中链接)。