我正在尝试转换 Go big。Int,表示 [2]int64 的 128 位整数。这个想法是能够匹配 Rust 的 i128::to_le_bytes(),它将 128 位有符号整数编码为小端字节顺序。该示例与 Rust 的i128::to_le_bytes()
匹配。每当我尝试将其转换回大时。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
相互转换。