我正在try 将Go Big.Int转换为[2]int64,它将表示一个128位的整数.其思想是能够与Rust的i128::to_le_bytes()相匹配,后者将128位有符号整数编码为小端字节顺序.这个例子与Rust的i128::to_le_bytes()相匹配.每当我try 将其转换回Big.Int时,我都得不到相同的值.在做最初的右移时有什么损失吗?谢谢.

package main
 
import (
    "encoding/binary"
    "fmt"
    "math/big"
)
 
func main() {
    initial := new(big.Int)
    initial.SetString("-42", 10)
 
    value, _ := new(big.Int).SetString("-42", 10)
 
    var result [2]int64
 
    result[0] = value.Int64()
    result[1] = value.Rsh(value, 64).Int64()
 
    leRepresentation := make([]byte, 16)
 
    binary.LittleEndian.PutUint64(leRepresentation[:8], uint64(result[0]))
    binary.LittleEndian.PutUint64(leRepresentation[8:], uint64(result[1]))
 
    fmt.Println(leRepresentation)
 
    fmt.Println(result)
 
    reverse := big.NewInt(result[1])
    reverse.Lsh(reverse, 64)
    reverse.Add(reverse, big.NewInt(result[0]))
 
    fmt.Println(reverse.String())
 
    fmt.Println(initial.String() == reverse.String())
}

推荐答案

这里有很多问题:

value不能用int64表示,因此value.Int64()的结果是未定义的.

您的较低位没有考虑带符号的结果Int64,因此您可以在结果中添加一个负数.您需要使用uint64(或者至少在将其添加到big.Int之前进行转换).

您在Rsh方法中变异了value,因此即使正确地重新创建了值,最后的比较也会失败.如果要比较原始值,请创建一个新的big.Int来存储它.

如果您希望big.Int的原始数据表示恰好为128位,则可以使用FillBytes方法.我们可以使用大端数据构建2个64位的值,如下所示:

b := make([]byte, 16)
value.FillBytes(b)  

var result [2]uint64
result[0] = binary.BigEndian.Uint64(b[:8])
result[1] = binary.BigEndian.Uint64(b[8:])

现在字节顺序是固定的,将符号位添加到结果中.然而,为了让这件事像int128一样工作,我们需要使用两个人的赞美来设置标志

const sign = uint64(1 << 63)
if value.Sign() < 0 {
    // convert the unsigned value to two's compliment
    result[0] = ^result[0]
    result[1] = ^result[1]

    result[1]++
    // check for carry
    if result[1] == 0 {
        result[0]++
    }
}

要创建新的big.Int,请颠倒整个过程:

neg := uint128[0]&sign != 0
if neg {
    // reverse the two's compliment
    if uint128[1] == 0 {
        uint128[0]--
    }
    uint128[1]--

    uint128[0] = ^uint128[0]
    uint128[1] = ^uint128[1]
}

b := make([]byte, 16)
binary.BigEndian.PutUint64(b[:8], uint128[0])
binary.BigEndian.PutUint64(b[8:], uint128[1])

result := new(big.Int).SetBytes(b)
if neg {
    result.Neg(result)
}

测试多个密钥值的示例:https://go.dev/play/p/E1E-5CilFlr

因为输出是以无符号值编写的,所以如果可以从值>;MaxInt128开始,您还应该添加一个判断,以确保不会使有符号值溢出.将这些值存储为[2]int64要麻烦得多,因为我们需要uint64值进行逐位操作,并且我们需要确保这int64个值不会通过它们自己的两个恭维进行翻转.在这种情况下,在给定函数周围的[2]int64[2]uint64之间来回转换会更容易.

Go相关问答推荐

Go -SDP服务器读缓冲区不会更改任何内容

如何存储来自异步Goroutine的返回值列表?

为什么我不能使用Docker从本地访问我的Gin应用程序?

如何使用Gio设置标题栏图标

Docker 执行失败并显示cmd/ENTRYPOINT 中的命令未找到

按位移计算结果中的差异

使用goroutines在Golang中验证 struct

如何为ANTLR4目标Go调试监听器

golang gin 获取 cookie json

如何使用 Docker 引擎 SDK 和 Golang 运行 docker 挂载卷

对所有标志进行 ORing 的简短方法

切片到数组指针的转换

Golang ACMEv2 HTTP-01 挑战不挑战服务器

如何在循环中旋转图像以便在 golang 中创建 GIF?

Golang - 使用正则表达式提取链接

实现接口的指针的泛型类型是什么?

Golang 泛型

从 map 返回空数组而不是空字符串数组

如何从应用程序调用谷歌云身份 API

在 etcd 键值存储中禁用历史记录