islishude / blog

my web notes
https://islishude.github.io/blog/
101 stars 15 forks source link

Go: big.Int 常用辅助函数 #227

Closed islishude closed 4 years ago

islishude commented 4 years ago

深拷贝

package main

import (
    "fmt"
    "math/big"
)

func main() {
    var a = big.NewInt(100)
    var b = new(big.Int).Set(a)
    a.Add(a, big.NewInt(100))
    fmt.Println(a, b) // 200 100
}

判断值是否大于、小于或等于 0

如果 x < 0 返回 -1 如果 x == 0 返回 0 如果 x > 0 返回 +1

package main

import (
    "fmt"
    "math/big"
)

func main() {
    var a = big.NewInt(1)
    var b = big.NewInt(-1)
    fmt.Println(a.Sign(), b.Sign())
}

如果判断是否为 0 那么也可以用这个,如果 BitLen() 返回 0 那么就是值为 0

var a = new(big.Int)
fmt.Println(a.BitLen()) // Output: 0

转化为固定长度的 bytes

转化为 bytes 很简单,直接使用 Bytes() 即可,但转化的 bytes 只有值的最小字节长度。

var a = big.NewInt(1)
fmt.Println(hex.EncodeToString(a.Bytes())) // Output: 01

如果要转化为固定长度,在 go 1.15 可以使用 FillBytes() 方法,这个比较配合适合 crypto 库使用

package main

import (
    "encoding/hex"
    "fmt"
    "math/big"
)

func main() {
    a := big.NewInt(1)
    v := a.FillBytes(make([]byte, 32))
    fmt.Println(hex.EncodeToString(v))
        // Output:
        // 0000000000000000000000000000000000000000000000000000000000000001
}

json.Number

bigint 进行 json 序列化的时候默认转化为 json.Number(float64),但是值如果过大,那么会精度损失,最佳的方式转化为 decimal string 即可。这个比较复杂,我这里封装了一个库,可以直接使用。

package main

import (
    "encoding/json"
    "fmt"

    "github.com/islishude/bigint"
)

func main() {
    a := bigint.New(100)
    data, err := json.Marshal(a)
    if err != nil {
        panic(err)
    }
    fmt.Println(string(data)) // "100"
}

当然也直接 hex string json 反序列化。

package main

import (
    "encoding/json"
    "fmt"

    "github.com/islishude/bigint"
)

func main() {
    type Object struct {
        Field bigint.Int
    }

    var d Object
    _ = json.Unmarshal([]byte(`{"Field": "0x3"}`), &d)
    fmt.Println(d.Field) // 3

    var e Object
    _ = json.Unmarshal([]byte(`{"Field": "0x"}`), &e)
    fmt.Println(e.Field) // <nil>
}

另外这个库也实现了 driver.Valuersql.Scanner,适用在数据库序列化使用。

// sql.Scanner
var i Int
_ = db.QueryRow("SELECT i FROM example WHERE id=1;").Scan(&i)

// driver.Valuer
var i bigint.Int
_ = db.Exec("INSERT INTO example (i) VALUES (?);", i) // nullable
i = bigint.New(1024)
_ = db.Exec("INSERT INTO example (i) VALUES (?);", i)