【Go言語】big.NewInt() の使い方と代替方法:多倍長整数の基礎

2025-06-01

big.NewInt() は、Go 言語の math/big パッケージで提供されている関数の一つです。この関数は、任意の精度を持つ整数(arbitrary-precision integer)を扱うための新しい big.Int 型の値を生成するために使われます。

より具体的に説明すると、

  • NewInt() 関数: この関数は、初期値として与えられた int64 型の整数を受け取り、その値を保持する新しい big.Int 型のポインタ (*big.Int) を返します。
  • big.Int: Go の標準の int, int64 などの整数型は、扱える値の範囲に上限と下限があります。これに対して、big.Int 型はメモリの許す限り、どれだけ大きな整数でも表現できるため、「多倍長整数」とも呼ばれます。

言葉だけでは少し分かりにくいかもしれませんので、簡単なコード例を見てみましょう。

package main

import (
	"fmt"
	"math/big"
)

func main() {
	// int64 型の整数値で新しい big.Int を作成
	initialValue := int64(12345)
	bigIntValue := big.NewInt(initialValue)

	// 作成された big.Int の値を文字列として表示
	fmt.Println(bigIntValue.String()) // 出力: 12345

	// 非常に大きな整数も扱うことができる
	largeValue := big.NewInt(9223372036854775807) // int64 の最大値
	largeValue.Mul(largeValue, big.NewInt(1000000)) // largeValue に 100万を掛ける
	fmt.Println(largeValue.String())
}

この例では、まず int64 型の initialValuebig.NewInt() に渡して、新しい big.Int 型の変数 bigIntValue を作成しています。

次に、int64 の最大値を初期値として持つ big.Int を作成し、さらに大きな数値を掛ける演算を行っています。標準の整数型ではオーバーフローしてしまうような計算も、big.Int を使えば正確に行うことができます。

  • 精度の必要な計算: 金融計算など、わずかな誤差も許されない場合に、big.Float と組み合わせて使用されることがあります。
  • 非常に大きな整数の計算: 暗号処理、数学的な計算などで、標準の整数型では扱えない大きな数を扱う必要がある場合。


nil ポインタの扱い

  • トラブルシューティング
    big.Int 型の変数を使用する前に、適切に初期化されていることを確認してください。big.NewInt() を使用して初期化するのが一般的です。nil ポインタに対してメソッド(例えば Add, Mul, String など)を呼び出そうとすると、ランタイムパニックが発生します。

    var n *big.Int // 初期化されていない
    // fmt.Println(n.String()) // これはパニックを引き起こす可能性があります
    
    m := big.NewInt(10) // 適切に初期化
    fmt.Println(m.String())
    
  • エラー
    big.NewInt() は常に有効な *big.Int 型のポインタを返すため、この関数自体が nil を返すことは通常ありません。しかし、もしコードの他の部分で *big.Int 型の変数が初期化されずに使用された場合、その変数は nil になる可能性があります。

型の不一致

  • トラブルシューティング
    big.NewInt() に渡す値が int64 型であることを確認してください。もし他の整数型(int, int32 など)の値を使用したい場合は、明示的に int64 型にキャストする必要があります。

    var i int = 100
    // big.NewInt(i) // これはコンパイルエラーになります
    bi := big.NewInt(int64(i)) // 正しい
    fmt.Println(bi.String())
    
  • エラー
    big.NewInt()int64 型の引数を期待します。異なる型の値を渡そうとすると、コンパイルエラーが発生します。

big.Int 型の比較

  • トラブルシューティング
    big.Int 型の値を比較する場合は、Cmp() メソッドを使用します。

    a := big.NewInt(10)
    b := big.NewInt(10)
    
    fmt.Println(a == b)       // ポインタの比較なので false になる可能性が高い
    fmt.Println(a.Cmp(b) == 0) // 値が等しい場合は 0 を返す
    
  • 間違いやすい点
    big.Int 型の変数を == 演算子で比較すると、値ではなくポインタのアドレスが比較されます。そのため、値が同じであっても false と評価されることがあります。

big.Int 型と他の数値型の演算

  • トラブルシューティング
    big.Int 型の値と演算を行う場合は、math/big パッケージで提供されているメソッド(Add, Sub, Mul, Div, SetInt64 など)を使用する必要があります。標準の数値型を big.Int 型に変換してから演算を行うこともあります。

    bi := big.NewInt(5)
    // result := bi + 10 // これはコンパイルエラーになります
    
    ten := big.NewInt(10)
    result := new(big.Int).Add(bi, ten) // 正しい
    fmt.Println(result.String())
    
    bi.Add(bi, big.NewInt(int64(10))) // bi 自体を更新する場合
    fmt.Println(bi.String())
    
  • エラー
    big.Int 型の値と標準の数値型(int, float64 など)を直接演算することはできません。

文字列からの変換

  • トラブルシューティング
    SetString() の戻り値(*big.Int, bool)を確認し、エラーが発生した場合は適切に処理する必要があります。

    s := "12345678901234567890"
    bi := new(big.Int)
    _, ok := bi.SetString(s, 10)
    if !ok {
        fmt.Println("文字列の変換に失敗しました")
    } else {
        fmt.Println(bi.String())
    }
    
    invalidS := "123abc456"
    bi2 := new(big.Int)
    _, ok2 := bi2.SetString(invalidS, 10)
    if !ok2 {
        fmt.Println("無効な文字列です")
    }
    
  • エラー
    SetString() に無効な形式の文字列や、指定した基数 (base) に合わない文字が含まれている場合、エラーが返ります。

  • 関連する関数
    big.NewInt()int64 からの変換ですが、大きな数を文字列で表現したい場合は big.NewInt(0).SetString(s, base) を使用します。この際にエラーが発生する可能性があります。

big.NewInt() 自体のエラーは少ないですが、big.Int 型の値を扱う際には、

  • 文字列からの変換時のエラー処理
  • 演算には math/big のメソッドを使用
  • 比較には Cmp() メソッドを使用
  • 型の意識
  • nil ポインタのチェック


例1: 基本的な big.NewInt() の使い方

この例では、int64 型の値を big.NewInt() を使って big.Int 型に変換し、その値を表示します。

package main

import (
	"fmt"
	"math/big"
)

func main() {
	// int64 型の整数値
	initialValue := int64(9876543210)

	// big.NewInt() を使って big.Int 型の新しいインスタンスを作成
	bigIntValue := big.NewInt(initialValue)

	// big.Int の値を文字列として表示
	fmt.Printf("bigIntValue の値: %s\n", bigIntValue.String())
}

説明

  1. initialValue という int64 型の変数を定義し、大きな整数値を代入しています。
  2. big.NewInt(initialValue) を呼び出すことで、initialValue の値を持つ新しい big.Int 型のポインタ bigIntValue が作成されます。
  3. bigIntValue.String() メソッドを呼び出すと、big.Int の値を文字列として取得できます。fmt.Printf を使って、この文字列を表示しています。

例2: big.Int 同士の演算

この例では、big.NewInt() で初期化した複数の big.Int 型の変数に対して、加算と乗算を行います。

package main

import (
	"fmt"
	"math/big"
)

func main() {
	// 2つの big.Int を初期化
	a := big.NewInt(100)
	b := big.NewInt(200)

	// 加算の結果を格納する新しい big.Int
	sum := new(big.Int)
	sum.Add(a, b)
	fmt.Printf("%s + %s = %s\n", a.String(), b.String(), sum.String()) // 出力: 100 + 200 = 300

	// 乗算の結果を格納する新しい big.Int
	product := new(big.Int)
	product.Mul(a, b)
	fmt.Printf("%s * %s = %s\n", a.String(), b.String(), product.String()) // 出力: 100 * 200 = 20000
}

説明

  1. ab という 2 つの big.Int 型の変数を big.NewInt() で初期化しています。
  2. 加算の結果を格納するために、new(big.Int) で新しい big.Int 型のポインタ sum を作成しています。Add() メソッドは、レシーバー (sum) に ab の和を格納します。
  3. 同様に、乗算の結果を格納するために product を作成し、Mul() メソッドで ab の積を計算して格納しています。
  4. それぞれの結果を String() メソッドで文字列に変換して表示しています。

例3: big.Intint64 の連携

この例では、big.Int 型の変数と int64 型の値を組み合わせて演算を行います。

package main

import (
	"fmt"
	"math/big"
)

func main() {
	// big.Int を初期化
	bi := big.NewInt(50)

	// int64 型の値
	increment := int64(10)

	// big.Int に int64 を加える (新しい big.Int を作成して結果を格納)
	added := new(big.Int).Add(bi, big.NewInt(increment))
	fmt.Printf("%s + %d = %s\n", bi.String(), increment, added.String()) // 出力: 50 + 10 = 60

	// big.Int に int64 を掛ける (元の big.Int を更新)
	bi.Mul(bi, big.NewInt(increment))
	fmt.Printf("元の big.Int に %d を掛けた結果: %s\n", increment, bi.String()) // 出力: 元の big.Int に 10 を掛けた結果: 500
}

説明

  1. bi という big.Int 型の変数を big.NewInt() で初期化しています。
  2. increment という int64 型の変数を定義しています。
  3. big.Intint64 を直接加算することはできないため、incrementbig.NewInt(increment)big.Int 型に変換してから Add() メソッドを使用しています。結果は新しい big.Int 型の変数 added に格納されます。
  4. Mul() メソッドを使って biincrement を掛けますが、この際も incrementbig.NewInt()big.Int 型に変換しています。Mul() をレシーバーに対して呼び出すと、レシーバー自身の値が更新されます。

例4: 非常に大きな数の扱い

この例では、big.NewInt() を使って初期化された非常に大きな数を扱い、演算を行います。

package main

import (
	"fmt"
	"math/big"
)

func main() {
	// 非常に大きな数を int64 で表現できないため、文字列から big.Int を作成
	largeNumberStr := "123456789012345678901234567890"
	largeNumber, ok := new(big.Int).SetString(largeNumberStr, 10)
	if !ok {
		fmt.Println("大きな数の初期化に失敗しました")
		return
	}

	multiplier := big.NewInt(1000000)
	result := new(big.Int).Mul(largeNumber, multiplier)

	fmt.Printf("%s * %s = %s\n", largeNumber.String(), multiplier.String(), result.String())
}
  1. int64 型の範囲を超える非常に大きな数を扱う場合、big.NewInt() に直接渡すことはできません。代わりに、文字列として表現された大きな数を SetString() メソッドを使って big.Int 型に変換します。この例では、基数 10 の文字列から largeNumber を作成しています。
  2. multiplier として 1000000 を持つ big.Intbig.NewInt() で作成しています。
  3. largeNumbermultiplier の積を Mul() メソッドで計算し、結果を result に格納して表示しています。


big.NewInt(0).SetInt64(x)

big.NewInt(0) でゼロ値で初期化された big.Int 型の新しいインスタンスを作成し、その後 SetInt64() メソッドを使って値を設定する方法です。

package main

import (
	"fmt"
	"math/big"
)

func main() {
	initialValue := int64(54321)
	bi := big.NewInt(0).SetInt64(initialValue)
	fmt.Println(bi.String()) // 出力: 54321
}

説明

  • この方法は、既存の big.Int 変数に int64 型の値を再設定する場合にもよく使われます。
  • .SetInt64(initialValue) は、この big.Int インスタンスの値を与えられた int64 型の initialValue に設定します。
  • big.NewInt(0) は、値が 0 の新しい big.Int 型のポインタを返します。

new(big.Int).SetInt64(x)

new(big.Int) でゼロ値で初期化された big.Int 型のポインタを作成し、その後 SetInt64() メソッドを使って値を設定する方法です。機能的には上記の方法とほぼ同じです。

package main

import (
	"fmt"
	"math/big"
)

func main() {
	initialValue := int64(12345)
	bi := new(big.Int).SetInt64(initialValue)
	fmt.Println(bi.String()) // 出力: 12345
}

説明

  • .SetInt64(initialValue) は、この big.Int インスタンスの値を与えられた int64 型の initialValue に設定します。
  • new(big.Int) は、ゼロ値を持つ big.Int 型の新しいポインタを返します。

new(big.Int).SetString(s, base)

非常に大きな整数や、特定の基数(例えば2進数、16進数など)で表現された文字列から big.Int 型の値を初期化する場合に使います。

package main

import (
	"fmt"
	"math/big"
)

func main() {
	// 10進数の文字列から初期化
	decimalStr := "98765432109876543210"
	biDecimal, ok := new(big.Int).SetString(decimalStr, 10)
	if !ok {
		fmt.Println("10進数の文字列の変換に失敗しました")
	} else {
		fmt.Println("10進数:", biDecimal.String())
	}

	// 16進数の文字列から初期化
	hexStr := "1A2B3C4D5E6F"
	biHex, ok := new(big.Int).SetString(hexStr, 16)
	if !ok {
		fmt.Println("16進数の文字列の変換に失敗しました")
	} else {
		fmt.Println("16進数:", biHex.String())
	}

	// 2進数の文字列から初期化
	binaryStr := "101101"
	biBinary, ok := new(big.Int).SetString(binaryStr, 2)
	if !ok {
		fmt.Println("2進数の文字列の変換に失敗しました")
	} else {
		fmt.Println("2進数:", biBinary.String())
	}
}

説明

  • このメソッドは、変換が成功したかどうかを示す bool 型の戻り値も返します。変換に失敗した場合は false になるため、エラーチェックを行うことが重要です。
  • .SetString(s, base) は、文字列 s を指定された基数 base の整数として解析し、big.Int の値に設定します。
  • new(big.Int) で新しい big.Int 型のポインタを作成します。

new(big.Int).Set(z)

既存の big.Int 型の変数の値を、新しい big.Int 型の変数にコピーする場合に使います。

package main

import (
	"fmt"
	"math/big"
)

func main() {
	a := big.NewInt(123)
	b := new(big.Int).Set(a) // a の値を b にコピー

	fmt.Printf("a: %s, b: %s\n", a.String(), b.String()) // 出力: a: 123, b: 123

	// b の値を変更しても a には影響しない (深いコピー)
	b.Add(b, big.NewInt(10))
	fmt.Printf("a: %s, b: %s\n", a.String(), b.String()) // 出力: a: 123, b: 133
}

説明

  • new(big.Int).Set(a) は、a の値と内部構造をコピーした新しい big.Int 型のポインタ b を作成します。これは深いコピーであるため、b の値を変更しても a には影響しません。
  • abig.NewInt(123) で初期化された big.Int 型の変数です。
  • 既存の big.Int の値を別の big.Int 変数にコピーする場合は、.Set(z) を使います。
  • 非常に大きな数や、特定の基数で表現された文字列から big.Int を作成する場合は、.SetString(s, base) を使う必要があります。
  • 既存の big.Int 変数に int64 型の値を再設定する場合は、.SetInt64(x) が便利です。
  • int64 型の既知の値から big.Int を作成する場合は、big.NewInt(x) が最も簡潔です。