【Go言語】big.NewInt() の使い方と代替方法:多倍長整数の基礎
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
型の initialValue
を big.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())
}
説明
initialValue
というint64
型の変数を定義し、大きな整数値を代入しています。big.NewInt(initialValue)
を呼び出すことで、initialValue
の値を持つ新しいbig.Int
型のポインタbigIntValue
が作成されます。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
}
説明
a
とb
という 2 つのbig.Int
型の変数をbig.NewInt()
で初期化しています。- 加算の結果を格納するために、
new(big.Int)
で新しいbig.Int
型のポインタsum
を作成しています。Add()
メソッドは、レシーバー (sum
) にa
とb
の和を格納します。 - 同様に、乗算の結果を格納するために
product
を作成し、Mul()
メソッドでa
とb
の積を計算して格納しています。 - それぞれの結果を
String()
メソッドで文字列に変換して表示しています。
例3: big.Int
と int64
の連携
この例では、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
}
説明
bi
というbig.Int
型の変数をbig.NewInt()
で初期化しています。increment
というint64
型の変数を定義しています。big.Int
とint64
を直接加算することはできないため、increment
をbig.NewInt(increment)
でbig.Int
型に変換してからAdd()
メソッドを使用しています。結果は新しいbig.Int
型の変数added
に格納されます。Mul()
メソッドを使ってbi
にincrement
を掛けますが、この際もincrement
をbig.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())
}
int64
型の範囲を超える非常に大きな数を扱う場合、big.NewInt()
に直接渡すことはできません。代わりに、文字列として表現された大きな数をSetString()
メソッドを使ってbig.Int
型に変換します。この例では、基数 10 の文字列からlargeNumber
を作成しています。multiplier
として1000000
を持つbig.Int
をbig.NewInt()
で作成しています。largeNumber
とmultiplier
の積を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
には影響しません。a
はbig.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)
が最も簡潔です。