【Go言語】big.Int.SetBit()を使いこなす:実践的なコード例で学ぶ

2025-06-01

func (z *Int) SetBit(x *Int, i int, b uint) *Int

この関数の引数と戻り値は以下の通りです。

  • 戻り値 *Int: 操作の結果が格納された z と同じ big.Int ポインタが返されます。これにより、メソッドチェーンが可能になります。
  • b uint: 設定するビットの値です。0 または 1 のいずれかになります。b0 の場合、i 番目のビットはクリアされ(0になる)、b1 の場合、i 番目のビットはセットされます(1になる)。b0 または 1 以外の場合、SetBit はパニック(panic)を起こします。
  • i int: 設定したいビットの位置(インデックス)です。最下位ビット(LSB)が0番目のビット、その左隣が1番目のビット、というように数えられます。非負の整数である必要があります。
  • x *Int: ビット操作の元となる big.Int です。x のコピーに対してビット操作が行われ、その結果が z に格納されます。zx が同じインスタンスであっても問題ありません。
  • z *Int: これはメソッドレシーバーであり、操作の結果が格納される big.Int ポインタです。通常、このメソッドが呼ばれる対象の big.Int インスタンス自身が使われます。

SetBit は基本的に以下の処理を行います。

  1. x の値をコピーします。
  2. コピーした値の i 番目のビットを b の値(0または1)に設定します。
  3. 結果を z に格納し、z を返します。
package main

import (
	"fmt"
	"math/big"
)

func main() {
	// 元の数値 (10進数で 10, 2進数で 1010)
	x := big.NewInt(10)
	fmt.Printf("元の値 x: %s (2進数: %b)\n", x.String(), x) // 出力: 元の値 x: 10 (2進数: 1010)

	// z を初期化
	z := new(big.Int)

	// 例1: 0番目のビットを1に設定 (1010 -> 1011)
	// 元の x (10) の0番目のビットは0ですが、これを1にします。
	// 結果は 11 (2進数で 1011)
	z.SetBit(x, 0, 1)
	fmt.Printf("0番目のビットを1に設定: %s (2進数: %b)\n", z.String(), z) // 出力: 0番目のビットを1に設定: 11 (2進数: 1011)

	// 例2: 1番目のビットを0に設定 (1010 -> 1000)
	// 元の x (10) の1番目のビットは1ですが、これを0にします。
	// 結果は 8 (2進数で 1000)
	z.SetBit(x, 1, 0)
	fmt.Printf("1番目のビットを0に設定: %s (2進数: %b)\n", z.String(), z) // 出力: 1番目のビットを0に設定: 8 (2進数: 1000)

	// 例3: 4番目のビットを1に設定 (1010 -> 11010)
	// 元の x (10) には4番目のビットがありませんが、設定できます。
	// 結果は 26 (2進数で 11010)
	z.SetBit(x, 4, 1)
	fmt.Printf("4番目のビットを1に設定: %s (2進数: %b)\n", z.String(), z) // 出力: 4番目のビットを1に設定: 26 (2進数: 11010)

	// 注意: b が 0 または 1 以外の場合
	// z.SetBit(x, 0, 2) // これはパニックを起こします
}


SetBit 関数自体が直接引き起こすエラーは比較的少ないですが、math/big パッケージの利用全般にわたる誤解や、特定の引数の使い方による問題が発生することがあります。

b 引数に 0 または 1 以外の値を与えた場合のパニック

エラーの原因
SetBit(x *Int, i int, b uint)b 引数は、設定するビットの値(0 または 1)を示す uint 型です。この b0 または 1 以外の値(例えば 23 など)を渡すと、SetBit 関数は実行時パニック (panic) を引き起こします。

トラブルシューティング

  • もし b が変数であれば、事前に if b != 0 && b != 1 { /* エラーハンドリング */ } のようなチェックを行うことで、パニックを防ぐことができます。
  • b に渡す値が常に 0 または 1 であることを確認してください。

コード例

package main

import (
	"fmt"
	"math/big"
)

func main() {
	x := big.NewInt(10)
	z := new(big.Int)

	// これはOK
	z.SetBit(x, 0, 1)
	fmt.Println("OK:", z)

	// これはパニックを引き起こす
	// z.SetBit(x, 0, 2)
	// fmt.Println("Panic:", z) // ここには到達しない
}

nil レシーバー (z) でメソッドを呼び出すことによるパニック

エラーの原因
Go のメソッド呼び出しにおいて、レシーバーが nil のポインタである場合、メソッド内でレシーバーのメンバーにアクセスしようとするとパニック (nil pointer dereference) が発生します。big.Int のメソッドは通常ポインタレシーバー (*Int) を取るため、初期化されていない *big.Int 変数に対して直接メソッドを呼び出すとこの問題に遭遇します。

トラブルシューティング

  • SetBit を呼び出す前に、レシーバーとなる big.Int ポインタを必ず初期化してください。new(big.Int) または big.NewInt(value) を使用します。

コード例

package main

import (
	"fmt"
	"math/big"
)

func main() {
	x := big.NewInt(10)

	var z *big.Int // nil ポインタ

	// エラー: runtime error: invalid memory address or nil pointer dereference
	// z.SetBit(x, 0, 1) 
	// fmt.Println(z)

	// 正しい初期化
	z = new(big.Int) // または z := new(big.Int)
	z.SetBit(x, 0, 1)
	fmt.Println("初期化後:", z)

	// あるいは、別の初期化方法
	z2 := big.NewInt(0) // NewIntを使うと初期値0で初期化される
	z2.SetBit(x, 0, 1)
	fmt.Println("NewIntで初期化後:", z2)
}

x 引数に nil を渡すことによる論理エラー (まれ)

エラーの原因
SetBit(z *Int, x *Int, i int, b uint)x 引数(元の数値)が nil であっても、Go の math/big パッケージの内部実装では、nil *big.Int は数値の 0 として扱われることがあります。しかし、これは常に安全であるとは限らず、予期せぬ挙動を引き起こす可能性があります。特に、コードの意図が明確でなくなるため、避けるべきです。

トラブルシューティング

  • x にも有効な big.Int インスタンスを渡すようにしてください。もし x の値が 0 であることを意図するならば、明示的に big.NewInt(0) を渡すべきです。

コード例

package main

import (
	"fmt"
	"math/big"
)

func main() {
	var xNil *big.Int // x が nil
	z := new(big.Int)

	// x が nil であってもパニックはしないが、意図が不明確になる可能性
	// 結果は 0 の0番目のビットを1に設定するので 1
	z.SetBit(xNil, 0, 1)
	fmt.Printf("x が nil の場合: %s (2進数: %b)\n", z.String(), z) // 出力: x が nil の場合: 1 (2進数: 1)

	// 意図が明確な書き方
	xZero := big.NewInt(0)
	z.SetBit(xZero, 0, 1)
	fmt.Printf("x が big.NewInt(0) の場合: %s (2進数: %b)\n", z.String(), z) // 出力: x が big.NewInt(0) の場合: 1 (2進数: 1)
}

元の big.Int (x) が意図せず変更されると誤解する

誤解の原因
SetBit のシグネチャは func (z *Int) SetBit(x *Int, i int, b uint) *Int です。この構造から、z が操作結果を格納する場所であり、x は元の値として使われるだけである、と理解できます。しかし、math/big の他の多くの関数(例: Add, Mul など)と同様に、SetBitzx が同じインスタンスである場合を考慮して設計されています。つまり、x.SetBit(x, i, b) のように呼び出すと、x 自身が変更されます。この動作は意図通りですが、他の関数と混同したり、注意が足りないと意図しない変更と誤解されることがあります。

トラブルシューティング

  • x の元の値を保持したい場合は、必ず新しい big.Int インスタンスを z として用意し、z.Set(x)x の値を z にコピーしてから z.SetBit を呼び出すか、あるいは x とは異なる別の big.Int インスタンスを z に指定してください。

コード例

package main

import (
	"fmt"
	"math/big"
)

func main() {
	originalX := big.NewInt(10) // 元の 10 (1010)
	fmt.Printf("元の originalX: %s (2進数: %b)\n", originalX.String(), originalX)

	// ケース1: z に新しいインスタンスを使う(originalX は変更されない)
	z1 := new(big.Int)
	z1.SetBit(originalX, 0, 1) // originalX の 0ビット目を1に
	fmt.Printf("z1 に新しいインスタンスを使用: z1=%s (2進数: %b), originalX=%s (2進数: %b)\n",
		z1.String(), z1, originalX.String(), originalX)
	// 出力: z1=11 (1011), originalX=10 (1010) - originalX は変更されない

	// ケース2: レシーバー (z) と元 (x) に同じインスタンスを使う(originalX が変更される)
	// これは big.Int の意図された動作であり、エラーではないが、注意が必要
	originalX.SetBit(originalX, 1, 0) // originalX の 1ビット目を0に (1010 -> 1000)
	fmt.Printf("originalX 自身を変更: originalX=%s (2進数: %b)\n", originalX.String(), originalX)
	// 出力: originalX=8 (1000) - originalX が変更された
}

ビットインデックス i が負の数の場合

エラーの原因
ドキュメントには明示的に記載されていませんが、ビットインデックス i は通常、非負の整数(0以上)を想定しています。負のインデックスを渡した場合の挙動は未定義または予期せぬ結果となる可能性があります。Go の math/big パッケージの多くのビット操作関数は、負のビットインデックスに対してパニックを起こすか、0 を返したりすることがあります。

  • ビットインデックス i が常に 0 以上であることを確認してください。


big.Int.SetBit() の基本

SetBit のシグネチャは以下の通りです。

func (z *Int) SetBit(x *Int, i int, b uint) *Int
  • 戻り値: 操作結果が格納された z と同じ big.Int のポインタ。
  • b uint: 設定するビットの値。0 または 1 でなければなりません。それ以外の値だとパニックします。
  • i int: 設定したいビットの位置(インデックス)。最下位ビットが 0 です。
  • x *Int: ビット操作の元となる big.Int のポインタ。x の値をコピーし、そのビットを変更します。
  • z *Int: 操作結果が格納される big.Int のポインタ(レシーバー)。

いくつかの具体的なシナリオを通じて SetBit の使い方を見ていきましょう。

例 1: 特定のビットを 1 に設定する(セットする)

ある整数の特定のビットを 1 に変更する最も基本的な例です。

package main

import (
	"fmt"
	"math/big"
)

func main() {
	// 元の数値 (10進数で 10, 2進数で 1010)
	x := big.NewInt(10)
	fmt.Printf("元の値 x: %s (2進数: %b)\n", x.String(), x) // 出力: 元の値 x: 10 (2進数: 1010)

	// 結果を格納する big.Int を初期化
	z := new(big.Int)

	// 0番目のビットを 1 に設定
	// x の 0番目のビットは現在 0 です。これを 1 に変更します。
	// 10 (1010) -> 11 (1011)
	bitIndex := 0
	bitValue := uint(1) // 1 に設定
	z.SetBit(x, bitIndex, bitValue)
	fmt.Printf("  %d 番目のビットを %d に設定: %s (2進数: %b)\n", bitIndex, bitValue, z.String(), z)
	// 出力:   0 番目のビットを 1 に設定: 11 (2進数: 1011)

	// 3番目のビットを 1 に設定 (元々 1010 なので、3番目は 1 ですが、そのまま)
	// 10 (1010) -> 10 (1010)
	bitIndex = 3
	bitValue = uint(1) // 1 に設定
	z.SetBit(x, bitIndex, bitValue)
	fmt.Printf("  %d 番目のビットを %d に設定: %s (2進数: %b)\n", bitIndex, bitValue, z.String(), z)
	// 出力:   3 番目のビットを 1 に設定: 10 (2進数: 1010)

	// 4番目のビットを 1 に設定 (元の数には存在しない高い位置のビット)
	// 10 (1010) -> 26 (11010)
	bitIndex = 4
	bitValue = uint(1) // 1 に設定
	z.SetBit(x, bitIndex, bitValue)
	fmt.Printf("  %d 番目のビットを %d に設定: %s (2進数: %b)\n", bitIndex, bitValue, z.String(), z)
	// 出力:   4 番目のビットを 1 に設定: 26 (2進数: 11010)
}

解説
z.SetBit(x, i, b) は、x の値を元にして i 番目のビットを b に設定し、その結果を z に格納します。x 自身は変更されません。存在しない高い位置のビットを指定した場合でも、big.Int は自動的に必要なサイズまで拡張されます。

例 2: 特定のビットを 0 に設定する(クリアする)

特定のビットを 0 に変更する場合です。

package main

import (
	"fmt"
	"math/big"
)

func main() {
	// 元の数値 (10進数で 13, 2進数で 1101)
	x := big.NewInt(13)
	fmt.Printf("元の値 x: %s (2進数: %b)\n", x.String(), x) // 出力: 元の値 x: 13 (2進数: 1101)

	// 結果を格納する big.Int を初期化
	z := new(big.Int)

	// 0番目のビットを 0 に設定
	// x の 0番目のビットは現在 1 です。これを 0 に変更します。
	// 13 (1101) -> 12 (1100)
	bitIndex := 0
	bitValue := uint(0) // 0 に設定
	z.SetBit(x, bitIndex, bitValue)
	fmt.Printf("  %d 番目のビットを %d に設定: %s (2進数: %b)\n", bitIndex, bitValue, z.String(), z)
	// 出力:   0 番目のビットを 0 に設定: 12 (2進数: 1100)

	// 2番目のビットを 0 に設定 (元々 13 (1101) の2番目は 1)
	// 13 (1101) -> 9 (1001)
	bitIndex = 2
	bitValue = uint(0) // 0 に設定
	z.SetBit(x, bitIndex, bitValue)
	fmt.Printf("  %d 番目のビットを %d に設定: %s (2進数: %b)\n", bitIndex, bitValue, z.String(), z)
	// 出力:   2 番目のビットを 0 に設定: 9 (2進数: 1001)

	// 4番目のビットを 0 に設定 (元の数には存在しない高い位置のビット、元々 0 なので変化なし)
	// 13 (1101) -> 13 (1101)
	bitIndex = 4
	bitValue = uint(0) // 0 に設定
	z.SetBit(x, bitIndex, bitValue)
	fmt.Printf("  %d 番目のビットを %d に設定: %s (2進数: %b)\n", bitIndex, bitValue, z.String(), z)
	// 出力:   4 番目のビットを 0 に設定: 13 (2進数: 1101)
}

解説
ビットを 0 に設定する場合も、同様に SetBit を使用します。存在しないビットを 0 に設定しても、そのビットは元々 0 なので数値は変わりません。

例 3: レシーバー (z) と元 (x) が同じインスタンスの場合

SetBitzx が同じ big.Int インスタンスであっても正しく動作します。これは、x のコピーに対して操作が行われ、結果が z に格納されるためです。

package main

import (
	"fmt"
	"math/big"
)

func main() {
	// 元の数値 (10進数で 10, 2進数で 1010)
	val := big.NewInt(10)
	fmt.Printf("元の値 val: %s (2進数: %b)\n", val.String(), val) // 出力: 元の値 val: 10 (2進数: 1010)

	// val 自身の 0番目のビットを 1 に設定する
	// 10 (1010) -> 11 (1011)
	bitIndex := 0
	bitValue := uint(1)
	val.SetBit(val, bitIndex, bitValue) // レシーバーと元が同じ val
	fmt.Printf("val 自身を更新後: %s (2進数: %b)\n", val.String(), val)
	// 出力: val 自身を更新後: 11 (2進数: 1011)

	// さらに val 自身の 2番目のビットを 0 に設定する
	// 11 (1011) -> 9 (1001)
	bitIndex = 2
	bitValue = uint(0)
	val.SetBit(val, bitIndex, bitValue) // レシーバーと元が同じ val
	fmt.Printf("val をさらに更新後: %s (2進数: %b)\n", val.String(), val)
	// 出力: val をさらに更新後: 9 (2進数: 1001)
}

解説
この方法は、元の数値を直接変更したい場合に便利です。val.SetBit(val, ...) のように記述することで、val が持つ値が更新されます。

例 4: 不正な b 引数によるパニック

SetBitb 引数には 0 または 1 のみ指定できます。それ以外の値を指定するとパニックが発生します。

package main

import (
	"fmt"
	"math/big"
)

func main() {
	x := big.NewInt(10)
	z := new(big.Int)

	// これはパニックを引き起こす!
	// b に 2 を指定しているため
	// z.SetBit(x, 0, 2)
	// fmt.Println(z) // ここには到達しない

	fmt.Println("この行は実行されません(コメントアウトされた SetBit が有効な場合)")
}

解説
b 引数の値の検証は重要です。ユーザー入力など、信頼できないソースから値が来る場合は、if b != 0 && b != 1 のようなチェックを追加して、パニックを回避し、適切なエラーハンドリングを行うべきです。

例 5: nil レシーバーによるパニック

big.Int のポインタレシーバー (*big.Int) は、メソッドを呼び出す前に初期化されている必要があります。

package main

import (
	"fmt"
	"math/big"
)

func main() {
	x := big.NewInt(10)

	var z *big.Int // 初期化されていない big.Int ポインタ (nil)

	// これは runtime error: invalid memory address or nil pointer dereference を引き起こす!
	// z.SetBit(x, 0, 1)
	// fmt.Println(z) // ここには到達しない

	// 正しい初期化方法
	z = new(big.Int) // new() で big.Int のゼロ値(0)のポインタを生成
	z.SetBit(x, 0, 1)
	fmt.Println("正しく初期化された z:", z)

	// あるいは big.NewInt で初期化
	z2 := big.NewInt(0) // 0 に初期化
	z2.SetBit(x, 0, 1)
	fmt.Println("big.NewInt で初期化された z2:", z2)
}

解説
var z *big.Int だけでは znil です。nil ポインタに対してメソッドを呼び出すと、内部で nil の指すメモリアドレスにアクセスしようとしてパニックします。new(big.Int) または big.NewInt(...) で明示的に初期化する必要があります。



主な代替方法は、ビットマスク(Bitmask) を使用した論理演算です。math/big パッケージには、これらの操作をサポートする関数が提供されています。

big.Int.SetBit() の代替方法

ビットマスクとビットごとの OR (Or) を使用してビットをセットする(1にする)

特定のビットを 1 に設定したい場合、そのビット位置にのみ 1 が立つビットマスクを作成し、元の数値とビットごとの OR 演算を行います。

  • 代替操作
    z.Or(x, big.NewInt(1).Lsh(big.NewInt(1), uint(i)))
  • 元の操作
    z.SetBit(x, i, 1)

解説

  1. big.NewInt(1): 1 を表す big.Int を作成します(これは2進数で ...001)。
  2. .Lsh(big.NewInt(1), uint(i)): この big.Inti ビット左にシフトします。これにより、i 番目のビットのみが 1 であるようなビットマスクが作成されます(例: i=3 なら 00001000)。
  3. z.Or(x, mask): 元の数値 x と作成したマスクに対してビットごとの OR 演算を行います。これにより、マスクで 1 になっている位置のビットは強制的に 1 になり、それ以外のビットは x の元の値が保持されます。

コード例

package main

import (
	"fmt"
	"math/big"
)

func main() {
	x := big.NewInt(10) // 2進数: 1010
	fmt.Printf("元の値 x: %s (2進数: %b)\n", x.String(), x)

	// SetBit を使用する場合 (基準)
	zSetBit := new(big.Int)
	zSetBit.SetBit(x, 0, 1) // 0番目のビットを1に設定 (1010 -> 1011)
	fmt.Printf("SetBit で 0番目を1に: %s (2進数: %b)\n", zSetBit.String(), zSetBit)

	// 代替方法: ビットマスクと Or を使用
	zOr := new(big.Int)
	bitIndex := 0
	// 1をbitIndex分だけ左シフトしてマスクを作成 (0番目なら 1)
	mask := new(big.Int).Lsh(big.NewInt(1), uint(bitIndex))
	zOr.Or(x, mask) // x とマスクのビットごとのOR
	fmt.Printf("Or とマスクで 0番目を1に: %s (2進数: %b)\n", zOr.String(), zOr)

	// 4番目のビットを1に設定 (1010 -> 11010)
	zSetBit.SetBit(x, 4, 1)
	fmt.Printf("SetBit で 4番目を1に: %s (2進数: %b)\n", zSetBit.String(), zSetBit)

	zOr2 := new(big.Int)
	bitIndex = 4
	mask = new(big.Int).Lsh(big.NewInt(1), uint(bitIndex))
	zOr2.Or(x, mask)
	fmt.Printf("Or とマスクで 4番目を1に: %s (2進数: %b)\n", zOr2.String(), zOr2)
}

ビットマスクとビットごとの AND (And)、AND NOT (AndNot) を使用してビットをクリアする(0にする)

特定のビットを 0 に設定したい場合、そのビット位置にのみ 0 が立ち、他がすべて 1 であるビットマスクを作成し、元の数値とビットごとの AND 演算を行います。あるいは、対象ビットに 1 が立つマスクを作成し、AndNot 演算を使用します。

  • 代替操作 (AndNot)
    z.AndNot(x, big.NewInt(1).Lsh(big.NewInt(1), uint(i)))

    解説

    1. big.NewInt(1).Lsh(big.NewInt(1), uint(i)): i 番目のビットのみが 1 であるビットマスク M を作成します。
    2. z.AndNot(x, M): これは x AND (NOT M) と等価です。つまり、M で 1 になっているビット位置の x のビットを 0 にクリアし、他のビットは x の値を保持します。これはビットクリアの目的で非常に直接的です。
  • 代替操作 (AND とビットマスク反転)
    z.And(x, big.NewInt(0).Not(big.NewInt(1).Lsh(big.NewInt(1), uint(i))))

    解説

    1. big.NewInt(1).Lsh(big.NewInt(1), uint(i)): i 番目のビットのみが 1 であるビットマスク M を作成します(例: i=2 なら 00000100)。
    2. big.NewInt(0).Not(M): マスク M のビットを反転させます。これにより、i 番目のビットのみが 0 で、他がすべて 1 であるマスク ~M が作成されます(例: i=2 なら ...11111011)。
    3. z.And(x, ~M): 元の数値 x と反転させたマスク ~M に対してビットごとの AND 演算を行います。これにより、マスクで 0 になっている位置のビットは強制的に 0 になり、それ以外のビットは x の元の値が保持されます。
  • 元の操作
    z.SetBit(x, i, 0)

コード例

package main

import (
	"fmt"
	"math/big"
)

func main() {
	x := big.NewInt(13) // 2進数: 1101
	fmt.Printf("元の値 x: %s (2進数: %b)\n", x.String(), x)

	// SetBit を使用する場合 (基準)
	zSetBit := new(big.Int)
	zSetBit.SetBit(x, 0, 0) // 0番目のビットを0に設定 (1101 -> 1100)
	fmt.Printf("SetBit で 0番目を0に: %s (2進数: %b)\n", zSetBit.String(), zSetBit)

	// 代替方法1: And とビットマスク反転を使用
	zAndNotMask := new(big.Int)
	bitIndex := 0
	// 1をbitIndex分だけ左シフトしてマスクを作成 (0番目なら 1)
	mask := new(big.Int).Lsh(big.NewInt(1), uint(bitIndex))
	// マスクを反転
	notMask := new(big.Int).Not(mask) // Not は引数のビットを反転
	zAndNotMask.And(x, notMask)        // x と反転マスクのビットごとのAND
	fmt.Printf("And と反転マスクで 0番目を0に: %s (2進数: %b)\n", zAndNotMask.String(), zAndNotMask)

	// 代替方法2: AndNot を使用 (より直接的)
	zAndNot := new(big.Int)
	bitIndex = 0
	mask = new(big.Int).Lsh(big.NewInt(1), uint(bitIndex))
	zAndNot.AndNot(x, mask) // x AND (NOT mask)
	fmt.Printf("AndNot で 0番目を0に: %s (2進数: %b)\n", zAndNot.String(), zAndNot)

	// 2番目のビットを0に設定 (1101 -> 1001)
	zSetBit.SetBit(x, 2, 0)
	fmt.Printf("SetBit で 2番目を0に: %s (2進数: %b)\n", zSetBit.String(), zSetBit)

	zAndNot2 := new(big.Int)
	bitIndex = 2
	mask = new(big.Int).Lsh(big.NewInt(1), uint(bitIndex))
	zAndNot2.AndNot(x, mask)
	fmt.Printf("AndNot で 2番目を0に: %s (2進数: %b)\n", zAndNot2.String(), zAndNot2)
}
  • ビットマスクと論理演算 (Or, And, AndNot):

    • 利点
      • 複数のビット操作を連続して行う場合に、より柔軟な表現が可能。
      • 例えば、複数のビットを一括でセットしたりクリアしたりするビットマスクを事前に作成しておき、それを使い回すことができる。
      • b 引数のバリデーションに関するパニックの心配がない(ただし、マスクの生成や論理演算のロジックが間違っていると結果が狂う)。
    • 欠点
      • 単一ビットの操作には冗長になる可能性がある。
      • ビットマスクの作成ロジックが複雑になると、可読性が低下する可能性がある。
    • 推奨
      より複雑なビット操作のパターンを実装する場合や、SetBit のような直接的な関数では表現しにくいカスタムのビット操作ロジックが必要な場合に検討します。
  • big.Int.SetBit():

    • 利点
      最も直接的で意図が明確。読みやすく、簡潔。
    • 欠点
      0 または 1 以外の b 引数を与えるとパニックする。
    • 推奨
      特定のビットを単独で設定/クリアする場合には、この関数が最も推奨されます。