【问题标题】:Integer overflow - golang整数溢出 - golang
【发布时间】:2019-12-15 15:53:25
【问题描述】:

我遇到了一些整数溢出错误。 我有一个使用 golang 和 go-micro 作为微服务框架构建的微服务应用程序。 我使用 NATS 作为消息代理。

我的微服务有效负载格式为

 map[string]interface{}

当我发布包含 uint64 的有效负载时会出现问题,例如

 var id uint64 = 512281913614499841

 message := map[string]inteface{}
 message["id"] = id

(这是由 cockroachdb 生成的唯一 ID),当订阅者接收到此消息作为字节并将其解组到 uint64 时,我意识到发生溢出并且值变为

512281913614499840 //this should be 512281913614499841

注意末尾的 0 而不是 1

我创建了 2 个函数(overFlowError 和 noOverflow) - 见下文。

overFlowError 函数模拟导致溢出的代码

并且 noOverflow 打印正确的结果,因为我使用这种格式的有效负载 map[string][struct] 而不是 map[string]interface

type UserType struct {
    Email string `json:"email"`
    ID    int64  `json:"id"`
}
func overFlowError() {

        var id int64 = 512281913614499841

        user := UserType{
            Email: "example",
            ID:    id,
        }

        message := map[string]interface{}{
            "data": user,
        }

        //mashal to byte to simulate service payload
        servicePayload, err := json.Marshal(message)

        if err != nil {
            log.Println(err)
        }

        var receivedMessage map[string]interface{}

        json.Unmarshal(servicePayload, &receivedMessage)

        var myUser UserType

        mapstructure.Decode(receivedMessage["data"], &myUser)

        log.Println("---receivedMessage:", myUser.ID) //prints 512281913614499840 - incorrect
    }

没有溢出

type UserType struct {
    Email string `json:"email"`
    ID    int64  `json:"id"`
}
func noOverflow() {

        var id int64 = 512281913614499841

        message := map[string]UserType{}

        message["data"] = UserType{
            Email: "example",
            ID:    id,
        }

        byteMessage, err := json.Marshal(message)

        if err != nil {
            log.Println(err)
        }

        var msgMap map[string]UserType

        json.Unmarshal(byteMessage, &msgMap)

        log.Println("---myUser:", msgMap["data"].ID) // prints 512281913614499841 - correct
    }

为了避免大量代码重写,我只剩下第一个选项,我在 overFlowError 函数中模拟并在上面解释过

这个问题有解决办法吗?

【问题讨论】:

  • 能否将您的代码库中的代码添加到此问题中?
  • 这是因为id 的值超出了float64 的精度范围。您可以将其编组为字符串并在接收时对其进行解析,或者将json.Decoderjson.Decoder.UseNumber一起使用而不是json.Unmarshal然后解析数字。
  • 谢谢leaf,我之前尝试了第一个解决方案,但是感觉不是最好的解决方案,因为字符串不是预期的类型。我正在寻找更自然、更优雅的解决方案。对于第二个解决方案,我正在考虑如何处理结构中的 uint64 字段,例如: type User struct{ID uint64} 因为这在反序列化时也会溢出。我看看 json.decoder 包
  • @ykel 您从ID 字段中查看了哪些数字属性,以便您不希望它成为字符串?你是加、减、除还是乘?
  • @zerkms,如果没有其他更好的方法,我将使用叶子的第一个解决方案。

标签: go


【解决方案1】:

这不是整数溢出。与leaf bebop noted in a comment 一样,问题实际上在于JSON 数字倾向于表示为浮点数,而float32 的位太少而无法保存正确的值。甚至 Go 默认使用的 float64 也不够用。

查看所有JSON integers: limit on size(不仅仅是已接受的)和Tom Christie's comment here 的答案。或者,查看较早的问题 JSON Not converting long numbers appropriately 及其已接受的答案。

根据您需要什么样的互操作性,您可以继续将它们解码为数字,但使用更复杂的解码器和/或自定义解组器,或者只是将其编码为 in JSON 作为字符串.在实践中,当在 Go + Python + TypeScript + 各种 C++ 库中使用 JSON 时,我们1 使用了字符串编码。我们最终发现这要可靠得多。 (我们不必对 32 位整数执行此操作,只需对 64 位整数执行此操作。)

要使用非默认解码器,请特别参阅json.NewDecoder 及其UseNumber 选项。


1这里的“我们”不是皇家的“我们”,而是 $job,除了 TypeScript 之外,我已经触及了所有这些。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-01-21
    • 2014-01-24
    • 1970-01-01
    • 2020-10-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多