英文:
Inconsistency in numbers between json Marshall and Unmarshall in Golang
问题
上述代码的问题在于使用了一个大于2^53的数字作为JSON对象的值。根据JSON规范,数字类型的值应该小于或等于2^53。如果超过这个范围,应该将其作为字符串类型处理。
在代码中,变量a
的值为9223372036854775807,这是一个大于2^53的数字。当将其作为值赋给JSON对象的balance
属性时,它被转换为浮点数类型。然后,通过JSON的编码和解码过程,将其转换回整数类型时,会出现精度丢失的问题,导致打印出的结果为-9223372036854775808。
为了解决这个问题,可以将balance
的值转换为字符串类型,以确保精度不会丢失。修改代码如下:
package main
import (
"encoding/json"
"fmt"
"strconv"
)
func t() {
a := int64(1<<63 - 1)
fmt.Println(a)
m := map[string]interface{}{
"balance": strconv.FormatInt(a, 10),
}
data, _ := json.Marshal(m)
m1 := make(map[string]interface{})
err := json.Unmarshal(data, &m1)
panicIF(err)
balanceStr := m1["balance"].(string)
balance, _ := strconv.ParseInt(balanceStr, 10, 64)
fmt.Println(balance)
}
func panicIF(err error) {
if err != nil {
panic(err)
}
}
修改后的代码将balance
的值转换为字符串类型,并在解码后将其转换回整数类型。这样就可以正确地打印出9223372036854775807作为结果。
英文:
package main
import (
"encoding/json"
"fmt"
)
func t() {
a := int64(1<<63 - 1)
fmt.Println(a)
m := map[string]interface{}{
"balance": a,
}
data, _ := json.Marshal(m)
m1 := make(map[string]interface{})
err := json.Unmarshal(data, &m1)
panicIF(err)
fmt.Println(int64(m1["balance"].(float64)))
}
func panicIF(err error) {
if err != nil {
panic(err)
}
}
Running above code will print:
9223372036854775807
-9223372036854775808
Can you please explain the issue? I read somewhere that in JSON we should not use numbers > 2^53 with number type, rather it should be string type.
答案1
得分: 1
当将数据反序列化为类型为map[string]any
的映射时,encoding/json
包将选择float64
类型来反序列化数字。
数字9223372036854775807
无法精确表示为float64
类型的值,因此将表示为接近它的其他数字,即9223372036854775808.0
。当将此数字转换为int64
时,由于它超出了有效范围,结果显然会是其他值,在您的情况下是可表示的int64
值的最小值。
添加一些额外的日志语句:
data, err := json.Marshal(m)
panicIF(err)
fmt.Println(string(data))
m1 := make(map[string]interface{})
err = json.Unmarshal(data, &m1)
panicIF(err)
fmt.Println(m1)
fmt.Printf("%f\n", m1["balance"].(float64))
fmt.Println(int64(m1["balance"].(float64)))
这将输出(在Go Playground上尝试):
9223372036854775807
{"balance":9223372036854775807}
map[balance:9.223372036854776e+18]
9223372036854775808.000000
-9223372036854775808
英文:
When unmarshaling into a map of type map[string]any
, the encoding/json
package will choose float64
type to unmarshal numbers.
The number 9223372036854775807
cannot be represented precisely by a value of type float64
, so some other number close to it will be represented, here namely 9223372036854775808.0
. When you convert this number to int64
, since it is outside of the valid range of int64
, the result will obviously be something else, in your case the minimum of the representable int64
values.
Add some extra logging statements:
data, err := json.Marshal(m)
panicIF(err)
fmt.Println(string(data))
m1 := make(map[string]interface{})
err = json.Unmarshal(data, &m1)
panicIF(err)
fmt.Println(m1)
fmt.Printf("%f\n", m1["balance"].(float64))
fmt.Println(int64(m1["balance"].(float64)))
This will output (try it on the Go Playground):
9223372036854775807
{"balance":9223372036854775807}
map[balance:9.223372036854776e+18]
9223372036854775808.000000
-9223372036854775808
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论