Go言語 big.Int.Text() の徹底解説:大きな整数を文字列に変換する方法
具体的には、以下のような働きをします。
- 戻り値
変換された整数を表す文字列と、エラー(通常はnil
)を返します。 - 基数指定
変換後の文字列が何進数で表現されるかを指定できる。例えば、10進数、2進数、16進数など。 - 目的
big.Int
型の値を人間が読みやすい文字列形式に変換する。
メソッドのシグネチャ
func (z *Int) Text(base int) string
error
: 通常はnil
が返されます。基数が不正な範囲外の場合など、エラーが発生する可能性はありますが、一般的な使用法ではあまり意識する必要はありません。string
: 変換された整数を表す文字列が返されます。(base int)
: 変換後の文字列の基数(進数)を指定する整数です。一般的に使用される基数は 2 (2進数), 8 (8進数), 10 (10進数), 16 (16進数) などです。基数が 2 から 36 の範囲外の場合、結果は予測できません。(z *Int)
: このメソッドが適用されるbig.Int
型のポインタz
です。
package main
import (
"fmt"
"math/big"
)
func main() {
n := new(big.Int)
n.SetString("12345678901234567890", 10) // 10進数の文字列から big.Int を作成
// 10進数で文字列化
decimalString := n.Text(10)
fmt.Println("10進数:", decimalString) // 出力: 10進数: 12345678901234567890
// 2進数で文字列化
binaryString := n.Text(2)
fmt.Println("2進数:", binaryString) // 出力: 2進数: 101011001110001101010001011010001011100110100010110
// 16進数で文字列化
hexadecimalString := n.Text(16)
fmt.Println("16進数:", hexadecimalString) // 出力: 16進数: 2bce3627b9d31a
// 基数を変えてみる (例: 36進数)
base36String := n.Text(36)
fmt.Println("36進数:", base36String) // 出力例: 36進数: 21hjy41f7m
}
無効な基数 (Invalid Base)
- トラブルシューティング
base
引数が 2 から 36 の範囲内であることを確認してください。- ユーザーからの入力や設定値を使用している場合は、範囲チェックを行うようにしてください。
- 原因
Text()
メソッドに渡すbase
引数が、許容される範囲 (通常は 2 から 36) 外の値である場合に発生します。 - エラー
panic
(実行時エラー) が発生する可能性があります。Goのバージョンによっては、エラーとして返される場合もあります。
例
package main
import (
"fmt"
"math/big"
)
func main() {
n := big.NewInt(100)
invalidBase := 1 // 許容範囲外の基数
// panic が発生する可能性 (Goのバージョンによる)
// result := n.Text(invalidBase)
// fmt.Println(result)
// 適切な範囲の基数を使用する
validBase := 16
hexString := n.Text(validBase)
fmt.Println("16進数:", hexString) // 出力: 16進数: 64
}
大きすぎる big.Int の文字列化によるメモリ消費
- トラブルシューティング
- 扱う
big.Int
の値が極端に大きくならないか、事前にサイズの見積もりを検討してください。 - 必要以上に大きな数を文字列化しないように、処理のロジックを見直してください。
- 大きな数をログ出力する場合などは、必要に応じて一部を切り詰めるなどの工夫を検討してください。
- 扱う
- 問題
非常に大きなbig.Int
の値をText()
で文字列化しようとすると、結果として非常に長い文字列が生成され、大量のメモリを消費する可能性があります。
文字列化された数の再変換時のエラー
- トラブルシューティング
Text()
で文字列化した際の基数を記録しておき、再変換時には同じ基数をSetString()
に渡すようにしてください。
- 問題
Text()
で得られた文字列をSetString()
などでbig.Int
に再変換する際に、元の基数と異なる基数を指定すると、誤った値になったりエラーが発生したりする可能性があります。
例
package main
import (
"fmt"
"math/big"
)
func main() {
n := new(big.Int)
n.SetString("FF", 16) // 16進数の "FF" を big.Int に
hexString := n.Text(16)
fmt.Println("16進数 (文字列):", hexString) // 出力: 16進数 (文字列): ff
// 正しい基数で再変換
n2 := new(big.Int)
_, ok := n2.SetString(hexString, 16)
if ok {
fmt.Println("再変換 (16進数):", n2) // 出力: 再変換 (16進数): 255
}
// 誤った基数で再変換 (エラーは発生しないが値が異なる)
n3 := new(big.Int)
_, ok = n3.SetString(hexString, 10)
if ok {
fmt.Println("再変換 (10進数 - 誤り):", n3) // 出力: 再変換 (10進数 - 誤り): 0
} else {
fmt.Println("再変換 (10進数 - 誤り): エラー発生")
}
}
符号付き整数の扱い
- トラブルシューティング
- 文字列化された値を解析する際には、先頭のマイナス記号の有無を考慮する必要があります。
- 再変換時に符号を正しく扱うように注意してください。
- 注意点
big.Int
は符号付き整数を扱えます。Text()
は、big.Int
が負の値の場合、先頭にマイナス記号 "-" を付加した文字列を返します。
- トラブルシューティング
- パフォーマンスが重要な場面では、文字列化の頻度を減らす、あるいはより効率的な方法を検討する必要があるかもしれません。
- 必要に応じて、文字列化する前に数の大きさをある程度制限するなどの対策を検討してください。
- 問題
極端に大きなbig.Int
の値をText()
で文字列化する処理は、計算コストが高くなる可能性があります。
例1: 基本的な基数変換
この例では、大きな整数を big.Int
型で扱い、それを異なる基数の文字列に変換して表示します。
package main
import (
"fmt"
"math/big"
)
func main() {
// 10進数の文字列から big.Int を作成
n := new(big.Int)
n.SetString("98765432109876543210", 10)
// 10進数で文字列化
decimalString := n.Text(10)
fmt.Println("10進数:", decimalString)
// 2進数で文字列化
binaryString := n.Text(2)
fmt.Println("2進数:", binaryString)
// 16進数で文字列化
hexadecimalString := n.Text(16)
fmt.Println("16進数:", hexadecimalString)
// 36進数で文字列化
base36String := n.Text(36)
fmt.Println("36進数:", base36String)
}
このコードでは、まず SetString()
を使って大きな10進数の文字列を big.Int
型の変数 n
に格納しています。その後、n.Text()
メソッドに異なる基数(10, 2, 16, 36)を指定して呼び出し、それぞれの基数で表現された文字列を fmt.Println()
で出力しています。
例2: 負の数の文字列化
big.Int
は負の数も扱えます。この例では、負の数を文字列化してみます。
package main
import (
"fmt"
"math/big"
)
func main() {
// 負の数の big.Int を作成
n := new(big.Int)
n.SetString("-12345", 10)
// 10進数で文字列化
decimalString := n.Text(10)
fmt.Println("10進数 (負の数):", decimalString)
// 16進数で文字列化
hexadecimalString := n.Text(16)
fmt.Println("16進数 (負の数):", hexadecimalString)
}
負の数を Text()
で文字列化すると、結果の文字列の先頭にマイナス記号 "-" が付加されることがわかります。
例3: 大きな数の演算結果を文字列で表示
big.Int
は通常の整数型よりも大きな数を扱えるため、その演算結果を Text()
で文字列として表示するのに役立ちます。
package main
import (
"fmt"
"math/big"
)
func main() {
// 非常に大きな数を扱う
a := new(big.Int)
a.SetString("123456789012345678901234567890", 10)
b := new(big.Int)
b.SetString("987654321098765432109876543210", 10)
// 掛け算を行う
result := new(big.Int)
result.Mul(a, b)
// 結果を10進数の文字列として表示
resultString := result.Text(10)
fmt.Println("掛け算の結果:", resultString)
}
この例では、二つの大きな数を big.Int
で定義し、Mul()
メソッドで掛け算を行っています。その結果も big.Int
型で得られるため、Text(10)
を使って10進数の文字列として表示しています。
例4: 特定のフォーマットでの出力 (応用)
Text()
メソッドは単に数値を指定された基数の文字列に変換するだけですが、得られた文字列をさらに加工することで、特定のフォーマットで出力することも可能です。
package main
import (
"fmt"
"math/big"
"strings"
)
func main() {
n := big.NewInt(255)
hexString := n.Text(16)
// 16進数2桁で表示 (必要に応じて0埋め)
if len(hexString) == 1 {
hexString = "0" + hexString
}
fmt.Println("16進数 (2桁):", strings.ToUpper(hexString)) // 出力: 16進数 (2桁): FF
bigNumber := new(big.Int)
bigNumber.SetString("10000000000", 10)
decimalString := bigNumber.Text(10)
// 3桁ごとにカンマを追加 (簡単な例)
var formattedString string
for i := len(decimalString) - 1; i >= 0; i-- {
formattedString = string(decimalString[i]) + formattedString
if (len(decimalString)-1-i)%3 == 0 && i != 0 {
formattedString = "," + formattedString
}
}
fmt.Println("カンマ区切り:", formattedString) // 出力: カンマ区切り: 10,000,000,000
}
fmt.Sprintf と書式指定子
fmt.Sprintf
関数は、書式指定子を使って様々な型の値を文字列に変換できます。big.Int
型の値も、いくつかの書式指定子に対応しています。
- %x / %X
16進数 (小文字/大文字、プレフィックス0x
は付きません) - %o
8進数 (プレフィックス0o
は付きません) - %b
2進数 (プレフィックス0b
は付きません) - %d
10進数
package main
import (
"fmt"
"math/big"
)
func main() {
n := big.NewInt(12345)
decimalString := fmt.Sprintf("%d", n)
fmt.Println("10進数 (Sprintf):", decimalString)
binaryString := fmt.Sprintf("%b", n)
fmt.Println("2進数 (Sprintf):", binaryString)
octalString := fmt.Sprintf("%o", n)
fmt.Println("8進数 (Sprintf):", octalString)
hexadecimalStringLower := fmt.Sprintf("%x", n)
fmt.Println("16進数 (Sprintf - 小文字):", hexadecimalStringLower)
hexadecimalStringUpper := fmt.Sprintf("%X", n)
fmt.Println("16進数 (Sprintf - 大文字):", hexadecimalStringUpper)
}
利点
- 他の型と組み合わせてフォーマット出力する際に便利です。
- より簡潔なコードで一般的な基数変換を行えます。
欠点
- 基数変換のエラーハンドリングは
fmt.Sprintf
自体にはありません。 Text()
のように、任意の基数 (2 から 36) を直接指定することはできません。一般的な基数に限定されます。
Int.String() メソッド (10進数限定)
big.Int
型には String()
メソッドも用意されています。これは、Text(10)
と同様に、big.Int
の値を10進数の文字列として返します。
package main
import (
"fmt"
"math/big"
)
func main() {
n := big.NewInt(98765)
decimalString := n.String()
fmt.Println("10進数 (String()):", decimalString)
}
利点
- デフォルトの文字列表現としてよく使われます。
- 10進数への変換に特化しており、
Text(10)
より記述が短くなります。
欠点
- 10進数以外の基数への変換はできません。
Int.Format() メソッド (より高度なフォーマット)
Int.Format()
メソッドは、より詳細なフォーマット指定を可能にします。これを使うと、符号の扱い、幅、精度、および様々な基数での文字列化を制御できます。
package main
import (
"fmt"
"math/big"
)
func main() {
n := big.NewInt(-12345)
// 10進数
decimalString := n.Format('d', 10)
fmt.Println("10進数 (Format 'd'):", decimalString)
// 16進数 (小文字)
hexadecimalStringLower := n.Format('x', 16)
fmt.Println("16進数 (Format 'x'):", hexadecimalStringLower)
// 16進数 (大文字)
hexadecimalStringUpper := n.Format('X', 16)
fmt.Println("16進数 (Format 'X'):", hexadecimalStringUpper)
// 2進数
binaryString := n.Format('b', 2)
fmt.Println("2進数 (Format 'b'):", binaryString)
}
Format()
メソッドの第一引数はフォーマットを指定する rune 型の動詞で、第二引数は基数です。
利点
- 符号の扱いなどをより細かく制御できます。
Text()
と同様に、様々な基数を指定できます。
欠点
Text()
ほど直接的ではありません。フォーマット指定子を理解する必要があります。
- より高度なフォーマット制御 (符号、幅など) や様々な基数
big.Int.Format()
が強力な選択肢となります。 - 10進数への変換のみ
big.Int.String()
が最も簡潔です。 - 一般的な基数 (10, 2, 8, 16) での簡単な変換
fmt.Sprintf
が簡潔に記述できます。 - 任意の基数 (2-36) でのシンプルな変換
big.Int.Text()
が最も直接的で使いやすいです。