Go言語 big.Int.Bit() を活用したビット操作のプログラミング例集

2025-06-01

  • 戻り値 uint: 指定されたビット位置 i の値(0 または 1)を uint 型で返します。
  • Bit(i int): big.Int 型のメソッドの一つです。引数として非負の整数 i を取ります。この i はビットの位置(インデックス)を表します。
  • big.Int: Go 言語の math/big パッケージで提供される型で、標準の int 型よりも大きな整数値を扱うことができます。

ビット位置の数え方

ビット位置は、右端のビットから 0 として数え始めます。

  • 例えば、整数値が 6 (バイナリで 110) の場合:
    • Bit(0) は右端のビットなので 0 を返します。
    • Bit(1) は右から 2番目のビットなので 1 を返します。
    • Bit(2) は右から 3番目のビットなので 1 を返します。
    • Bit(3) より左のビットは 0 とみなされ、Bit(3)0 を返します。


package main

import (
	"fmt"
	"math/big"
)

func main() {
	n := big.NewInt(6) // 整数値 6 を持つ big.Int を作成 (バイナリ: 110)

	fmt.Println(n.Bit(0)) // 出力: 0 (右端のビット)
	fmt.Println(n.Bit(1)) // 出力: 1 (右から2番目のビット)
	fmt.Println(n.Bit(2)) // 出力: 1 (右から3番目のビット)
	fmt.Println(n.Bit(3)) // 出力: 0 (指定した位置より左のビット)
	fmt.Println(n.Bit(100)) // 出力: 0 (大きなインデックスを指定した場合も 0)
}




例1: 特定のビットが立っているかどうかの確認

この例では、big.Int 型の数値の特定のビット位置が 1 であるかどうかを確認します。

package main

import (
	"fmt"
	"math/big"
)

func main() {
	n := big.NewInt(13) // 整数値 13 (バイナリ: 1101)
	bitPosition := 2    // 確認したいビット位置 (右から 3番目)

	if n.Bit(bitPosition) == 1 {
		fmt.Printf("%d の %d ビット目は立っています (1)\n", n, bitPosition)
	} else {
		fmt.Printf("%d の %d ビット目は立っていません (0)\n", n, bitPosition)
	}

	bitPosition = 0 // 確認したいビット位置 (右端)
	if n.Bit(bitPosition) == 1 {
		fmt.Printf("%d の %d ビット目は立っています (1)\n", n, bitPosition)
	} else {
		fmt.Printf("%d の %d ビット目は立っていません (0)\n", n, bitPosition)
	}
}

解説

  • if 文で、返されたビット値が 1 であるかどうかを判定し、結果を出力します。
  • n.Bit(bitPosition) は、指定されたビット位置の値(0 または 1)を返します。
  • bitPosition 変数で確認したいビットの位置を指定します。ビット位置は右端から 0 で始まることに注意してください。
  • big.NewInt(13) で、値が 13 の big.Int 型の変数 n を作成します。13 をバイナリで表現すると 1101 です。

例2: big.Int のすべてのビットを走査して表示

この例では、big.Int 型の数値のすべてのビットを走査し、それぞれのビットの値と位置を表示します。BitLen() メソッドを使って、big.Int の有効なビット長を取得します。

package main

import (
	"fmt"
	"math/big"
)

func main() {
	n := big.NewInt(42) // 整数値 42 (バイナリ: 101010)
	bitLength := n.BitLen()

	fmt.Printf("%d のビット:\n", n)
	for i := 0; i < bitLength; i++ {
		bitValue := n.Bit(i)
		fmt.Printf("ビット位置 %d: %d\n", i, bitValue)
	}
}

解説

  • for ループで、0 から bitLength - 1 までのインデックスで n.Bit(i) を呼び出し、各ビットの値を取得して表示します。
  • n.BitLen() は、n の最上位ビットのインデックス + 1、つまり有効なビット数を返します。
  • big.NewInt(42) で、値が 42 の big.Int 型の変数 n を作成します。42 をバイナリで表現すると 101010 です。

例3: ビット演算と Bit() の組み合わせ

この例では、ビット演算の結果として得られた big.Int の特定のビットを確認します。

package main

import (
	"fmt"
	"math/big"
)

func main() {
	a := big.NewInt(5)  // 整数値 5 (バイナリ: 101)
	b := big.NewInt(3)  // 整数値 3 (バイナリ: 011)

	// AND 演算
	andResult := new(big.Int).And(a, b) // 結果: 001 (1)
	fmt.Printf("%d AND %d = %d (バイナリ: %b)\n", a, b, andResult, andResult)
	fmt.Printf("AND 結果の 0 ビット目: %d\n", andResult.Bit(0)) // 出力: 1
	fmt.Printf("AND 結果の 1 ビット目: %d\n", andResult.Bit(1)) // 出力: 0

	// OR 演算
	orResult := new(big.Int).Or(a, b)   // 結果: 111 (7)
	fmt.Printf("%d OR %d = %d (バイナリ: %b)\n", a, b, orResult, orResult)
	fmt.Printf("OR 結果の 0 ビット目: %d\n", orResult.Bit(0))  // 出力: 1
	fmt.Printf("OR 結果の 1 ビット目: %d\n", orResult.Bit(1))  // 出力: 1
	fmt.Printf("OR 結果の 2 ビット目: %d\n", orResult.Bit(2))  // 出力: 1
}
  • %b フォーマット指定子を使うと、big.Int の値をバイナリ形式で出力できます。
  • その後、Bit() メソッドを使って、演算結果の特定のビットの値を確認しています。
  • new(big.Int).And(a, b)new(big.Int).Or(a, b) は、それぞれビットごとの AND 演算と OR 演算を行い、その結果を新しい big.Int 型の変数に格納します。
  • big.NewInt() で二つの big.Int 型の変数 ab を初期化します。


big.Int.Bits() メソッド (ビット列への直接アクセス)

  • big.Int.Bits() メソッドは、big.Int の内部表現である []uint 型のスライスを返します。このスライスは、big.Int の絶対値をリトルエンディアン(最下位のワードが最初の要素)で表現したワードの配列です。各ワードは通常、32 ビットまたは 64 ビットの符号なし整数です。
package main

import (
	"fmt"
	"math/big"
)

func main() {
	n := big.NewInt(13) // 整数値 13 (バイナリ: ...0000 1101)
	bits := n.Bits()
	fmt.Printf("内部ビット表現: %v\n", bits)

	// 特定のビット位置へのアクセス (少し複雑になります)
	bitIndex := 2 // 確認したいビット位置
	wordIndex := bitIndex / 32 // 通常のワードサイズを仮定
	bitInWord := uint(bitIndex % 32)

	if wordIndex < len(bits) && (bits[wordIndex]&(1<<bitInWord)) != 0 {
		fmt.Printf("%d の %d ビット目は立っています (1)\n", n, bitIndex)
	} else {
		fmt.Printf("%d の %d ビット目は立っていません (0)\n", n, bitIndex)
	}
}

解説

  • ビットが立っているかどうかは、ワードの値とビットマスク (1<<bitInWord) の論理 AND 演算の結果が 0 でないかで判定します。
  • ビット位置 bitIndex を、対応するワードのインデックス wordIndex と、そのワード内のビット位置 bitInWord に変換する必要があります。
  • n.Bits() は、n の内部表現のスライスを返します。

注意点
big.Int の内部表現は Go のバージョンやアーキテクチャによって異なる可能性があるため、この方法を直接利用する場合は注意が必要です。

ビットマスクと論理演算

package main

import (
	"fmt"
	"math/big"
)

func main() {
	n := big.NewInt(13) // 整数値 13 (バイナリ: ...0000 1101)

	// 下位 3 ビットの状態を確認
	mask := big.NewInt(7) // バイナリ: ...0000 0111
	lower3Bits := new(big.Int).And(n, mask)
	fmt.Printf("%d の下位 3 ビット: %d (バイナリ: %b)\n", n, lower3Bits, lower3Bits)

	// 特定のビットがすべて立っているか確認
	checkMask := big.NewInt(6) // 確認したいビット位置に対応するマスク (バイナリ: ...0000 0110)
	if new(big.Int).And(n, checkMask).Cmp(checkMask) == 0 {
		fmt.Printf("%d の 1 ビット目と 2 ビット目は両方立っています\n", n)
	} else {
		fmt.Printf("%d の 1 ビット目と 2 ビット目の少なくとも一方は立っていません\n", n)
	}
}

解説

  • Cmp() メソッドは、big.Int 同士を比較するために使用します。
  • ビットマスクを作成し、And() などの論理演算メソッドを使って、特定のビット範囲の値を取得したり、特定のビットが立っているかどうかを判定したりします。

文字列変換 (Format() メソッド)

  • big.Int をバイナリ文字列に変換し、その文字列を解析することで、各ビットの値を確認できます。
package main

import (
	"fmt"
	"math/big"
)

func main() {
	n := big.NewInt(13) // 整数値 13

	binaryString := n.Format(nil, 2) // 2進数文字列に変換
	fmt.Printf("%d のバイナリ表現: %s\n", n, binaryString)

	// 文字列のインデックスでビット値にアクセス (右端がインデックス 0 にならない点に注意)
	bitIndexFromRight := 2
	bitIndexFromLeft := len(binaryString) - 1 - bitIndexFromRight
	if bitIndexFromLeft >= 0 && bitIndexFromLeft < len(binaryString) && binaryString[bitIndexFromLeft] == '1' {
		fmt.Printf("%d の右から %d ビット目は立っています (1)\n", n, bitIndexFromRight)
	} else {
		fmt.Printf("%d の右から %d ビット目は立っていません (0)\n", n, bitIndexFromRight)
	}
}

解説

  • バイナリ文字列のインデックスを使って、特定のビット位置の値にアクセスできます。ただし、文字列のインデックスは左端から始まるため、右端からのビット位置を考慮してインデックスを計算する必要があります。
  • n.Format(nil, 2) は、big.Int n をバイナリ形式の文字列に変換します。