go - 将 big.Int 转换为 [2]int64,反之亦然以及二进制补码

标签 go bigint

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

你正在变异valueRsh方法,因此即使正确地重新创建了值,最后的比较也会失败。创建一个新的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值(value)观不会因为他们自己的赞美而被覆盖。在这种情况下,转换 [2]int64 会更容易。往返[2]uint64围绕给定的函数。

关于go - 将 big.Int 转换为 [2]int64,反之亦然以及二进制补码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74709367/

相关文章:

objective-c - 为什么 Swift 函数定义语法是多余的?

go - 为什么 Scanf() 不能为我正常工作?

postgresql - Postgres 中的 Go 和 IN 子句

php - BIGINT 在 mysql 中创建为 INT

c++ - C++ 中 BigInt 库的问题

go - 检查包是否会构建

google-app-engine - 在代理后面使用 Go App Engine 的问题

javascript - 在 JavaScript 中使用 "new"关键字创建包装的 BigInt 或 Symbol 之间有区别吗?

c++ - BigInt 转换(gmp Bigint 到 botan bigint)

assembly - 我怎样才能将两个数字相加,每个数字有 12 个字节?