首頁  >  文章  >  後端開發  >  將 big.Int 轉換為 int64,反之亦然以及二進位補碼

將 big.Int 轉換為 int64,反之亦然以及二進位補碼

王林
王林轉載
2024-02-09 17:51:09385瀏覽

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

php小編柚子將為您介紹如何在PHP中將big.Int轉換為int64,以及如何將int64轉換為big.Int。在電腦程式設計中,big.Int和int64是兩種不同的資料類型,big.Int用於處理大型整數,而int64是一種64位元的有符號整數類型。在進行型別轉換時,我們需要注意二進制補碼的概念,它是計算機中表示有符號整數的一種方式。接下來,我們將詳細介紹這兩種類型之間的轉換過程。

問題內容

我正在嘗試將表示 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 之前對其進行轉換)。

您正在 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 相互轉換會更容易。

以上是將 big.Int 轉換為 int64,反之亦然以及二進位補碼的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:stackoverflow.com。如有侵權,請聯絡admin@php.cn刪除