[インデックス 11544] ファイルの概要
このコミットは、Go言語の標準ライブラリmath/big
パッケージのInt
型に、その内部表現であるビット列への直接的な(raw)アクセスを提供するBits()
およびSetBits()
メソッドを追加するものです。これにより、Int
型が提供していない高度な機能を外部で効率的に実装することが可能になります。また、レシーバの命名規則に関する一貫性のない箇所が修正されています。
コミット
commit 71c19b610f1f5c0d61cc339d122eba730fc78c71
Author: Robert Griesemer <gri@golang.org>
Date: Wed Feb 1 11:43:40 2012 -0800
mat/big: add raw access to Int bits
This is a minimal API extension, it makes it possible
to implement missing Int functionality externally w/o
compromising efficiency. It is the hope that this will
reduce the number of feature requests going directly
into the big package.
Also: Fixed some naming inconsistencies: The receiver
is only called z when it is also the result.
R=golang-dev, agl
CC=golang-dev
https://golang.org/cl/5607055
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/71c19b610f1f5c0d61cc339d122eba730fc78c71
元コミット内容
mat/big: add raw access to Int bits
This is a minimal API extension, it makes it possible
to implement missing Int functionality externally w/o
compromising efficiency. It is the hope that this will
reduce the number of feature requests going directly
into the big package.
Also: Fixed some naming inconsistencies: The receiver
is only called z when it is also the result.
R=golang-dev, agl
CC=golang-dev
https://golang.org/cl/5607055
変更の背景
この変更の主な背景は、math/big
パッケージのInt
型が提供する機能の範囲と、ユーザーからの機能追加要求のバランスを取ることにあります。math/big.Int
は任意精度整数を扱うための強力な型ですが、すべての可能な数学的演算やビット操作を網羅しているわけではありません。
コミットメッセージにあるように、「これは最小限のAPI拡張であり、効率を損なうことなく、不足しているIntの機能を外部で実装することを可能にする」という点が重要です。つまり、パッケージの内部実装に深く依存するような低レベルの操作を、パッケージ自体に直接追加するのではなく、ユーザーがInt
の内部表現にアクセスできるようにすることで、外部で柔軟かつ効率的に実装できるようにする意図があります。これにより、math/big
パッケージへの直接的な機能追加要求の数を減らすことが期待されています。
また、Go言語のコーディング規約におけるレシーバの命名規則の一貫性を保つことも目的の一つです。特に、メソッドのレシーバがそのメソッドの戻り値でもある場合にのみz
という名前を使用するという慣習に合わせるための修正が含まれています。
前提知識の解説
math/big
パッケージと任意精度整数
Go言語のmath/big
パッケージは、任意精度の数値を扱うための型を提供します。通常のGoの組み込み整数型(int
, int64
など)は、固定されたビット幅(例: 64ビット)で表現されるため、扱える数値の範囲に限界があります。これに対し、任意精度整数は、必要に応じてメモリを動的に割り当てることで、理論上はどんなに大きな整数でも表現できます。
math/big.Int
型は、この任意精度整数を表現するための構造体です。内部的には、数値は通常、Word
型のスライス(配列)として表現されます。Word
は、システムが効率的に扱えるワードサイズ(例: 32ビットまたは64ビット)の符号なし整数型です。数値の絶対値がこのWord
スライスの要素として格納され、符号は別のフィールドで管理されます。
「Raw Access」の概念
「Raw Access」とは、データ構造の内部表現に直接アクセスすることを指します。通常、ライブラリやAPIは、内部の詳細を隠蔽し、抽象化されたインターフェース(メソッドなど)を通じてデータにアクセスすることを推奨します。これは、内部実装の変更が外部に影響を与えないようにするため、また、誤った使い方を防ぐためです。
しかし、特定の高度なユースケースでは、この抽象化が性能のボトルネックになったり、特定の機能の実装を不可能にしたりすることがあります。このような場合、ライブラリは「raw access」を許可するAPIを提供することがあります。これは通常、パフォーマンスが非常に重要であるか、またはライブラリが提供しない特定の低レベル操作が必要な場合に限られます。ただし、raw accessは内部実装に依存するため、将来のバージョンで互換性が失われるリスクを伴います。
Go言語のレシーバ命名規則
Go言語では、メソッドのレシーバ(メソッドが呼び出されるオブジェクト)の命名に関して一般的な慣習があります。
- レシーバは通常、その型の最初の文字(例:
*Int
型ならi
)を使用します。 - ただし、メソッドがレシーバ自身を変更し、そのレシーバを結果として返す場合(例:
Set
メソッドなど)、レシーバ名をz
とすることがよくあります。これは、数学的な文脈で結果を表す変数としてz
が使われることに由来します。 - レシーバが変更されず、単にその値を使用するだけのメソッド(例:
String()
やBytes()
など)では、x
やy
といった一般的な変数名が使われることがあります。
このコミットでは、この慣習に沿って、Bytes()
, BitLen()
, ProbablyPrime()
, Bit()
, GobEncode()
といったメソッドのレシーバ名がz
からx
に変更されています。これは、これらのメソッドがレシーバの値を変更せず、単にその値を読み取るだけであるためです。
技術的詳細
Bits()
メソッドの追加
func (x *Int) Bits() []Word
- 目的:
Int
型の絶対値を、リトルエンディアンのWord
スライスとして返します。 - 詳細:
- レシーバは
x
と命名されています。これは、このメソッドがInt
の値を変更しないためです。 - 戻り値は
[]Word
型です。Word
はmath/big
パッケージ内で定義されている符号なし整数型で、通常はuint
またはuint64
のエイリアスです。 - 「リトルエンディアン」とは、数値の最下位ワード(最下位ビットを含むワード)がスライスの先頭(インデックス0)に格納されることを意味します。
- 「The result and x share the same underlying array.」というコメントは非常に重要です。これは、返される
[]Word
スライスが、Int
型の内部で使用されている基盤となる配列を直接参照していることを意味します。したがって、このスライスを外部で変更すると、元のInt
オブジェクトの値も変更されてしまう可能性があります。これは「unchecked but fast access」の典型的な例であり、非常に注意して使用する必要があります。 - このメソッドは、
x.abs
(Int
型の内部フィールドで絶対値をnat
型、実質的には[]Word
として保持している)を直接返しています。
- レシーバは
SetBits()
メソッドの追加
func (z *Int) SetBits(abs []Word) *Int
- 目的:
Int
型の値を、与えられたWord
スライスabs
に基づいて設定し、Int
オブジェクト自身を返します。 - 詳細:
- レシーバは
z
と命名されています。これは、このメソッドがレシーバであるInt
オブジェクトの値を変更し、そのオブジェクトを戻り値として返すためです。 - 引数
abs
は、リトルエンディアンのWord
スライスとして解釈されます。 - 「The result and abs share the same underlying array.」というコメントは、
Bits()
と同様に、渡されたabs
スライスがInt
の内部配列として直接使用される可能性があることを示唆しています。これにより、コピーのオーバーヘッドを避けて効率的な設定が可能になりますが、abs
スライスを外部で変更すると、設定後のInt
オブジェクトの値も意図せず変更されるリスクがあります。 z.abs = nat(abs).norm()
:nat(abs)
:[]Word
スライスをnat
型にキャストしています。nat
型はmath/big
パッケージ内で定義されている内部的な型で、任意精度整数の絶対値を表現するために使われます。.norm()
:nat
型のnorm()
メソッドは、スライスの末尾にあるゼロワードを削除し、数値の正規化を行います。例えば、[1, 2, 0, 0]
のようなスライスが与えられた場合、[1, 2]
に正規化されます。これにより、数値の表現が一意になり、不要なメモリ使用を避けることができます。
z.neg = false
:SetBits
は絶対値のビット列を受け取るため、設定されるInt
の符号は非負(0または正)に設定されます。負の値を表現したい場合は、別途SetSign
などのメソッドを使用する必要があります。
- レシーバは
レシーバ命名規則の修正
以下のメソッドで、レシーバ名がz
からx
に変更されています。これは、これらのメソッドがレシーバの値を変更せず、単にその値を読み取るだけであるためです。
Bytes()
BitLen()
ProbablyPrime()
Bit()
GobEncode()
これらの変更は、Go言語の標準ライブラリにおける一貫したコーディングスタイルを維持するためのものです。
コアとなるコードの変更箇所
--- a/src/pkg/math/big/int.go
+++ b/src/pkg/math/big/int.go
@@ -65,6 +65,26 @@ func (z *Int) Set(x *Int) *Int {
return z
}
+// Bits provides raw (unchecked but fast) access to x by returning its
+// absolute value as a little-endian Word slice. The result and x share
+// the same underlying array.
+// Bits is intended to support implementation of missing low-level Int
+// functionality outside this package; it should be avoided otherwise.
+func (x *Int) Bits() []Word {
+ return x.abs
+}
+
+// SetBits provides raw (unchecked but fast) access to z by setting its
+// value to abs, interpreted as a little-endian Word slice, and returning
+// z. The result and abs share the same underlying array.
+// SetBits is intended to support implementation of missing low-level Int
+// functionality outside this package; it should be avoided otherwise.
+func (z *Int) SetBits(abs []Word) *Int {
+ z.abs = nat(abs).norm()
+ z.neg = false
+ return z
+}
+
// Abs sets z to |x| (the absolute value of x) and returns z.
func (z *Int) Abs(x *Int) *Int {
z.Set(x)
@@ -528,18 +548,18 @@ func (z *Int) SetBytes(buf []byte) *Int {
}
// Bytes returns the absolute value of z as a big-endian byte slice.
-func (z *Int) Bytes() []byte {
- buf := make([]byte, len(z.abs)*_S)
- return buf[z.abs.bytes(buf):]
+func (x *Int) Bytes() []byte {
+ buf := make([]byte, len(x.abs)*_S)
+ return buf[x.abs.bytes(buf):]
}
// BitLen returns the length of the absolute value of z in bits.
// The bit length of 0 is 0.
-func (z *Int) BitLen() int {
- return z.abs.bitLen()
+func (x *Int) BitLen() int {
+ return x.abs.bitLen()
}
-// Exp sets z = x**y mod m. If m is nil, z = x**y.\n+// Exp sets z = x**y mod m and returns z. If m is nil, z = x**y.
+// Exp sets z = x**y mod m and returns z. If m is nil, z = x**y.
// See Knuth, volume 2, section 4.6.3.
func (z *Int) Exp(x, y, m *Int) *Int {
if y.neg || len(y.abs) == 0 {
@@ -617,11 +637,11 @@ func GcdInt(d, x, y, a, b *Int) {
*d = *A
}
-// ProbablyPrime performs n Miller-Rabin tests to check whether z is prime.\n-// If it returns true, z is prime with probability 1 - 1/4^n.\n-// If it returns false, z is not prime.\n-func ProbablyPrime(z *Int, n int) bool {\n- return !z.neg && z.abs.probablyPrime(n)\n+// ProbablyPrime performs n Miller-Rabin tests to check whether x is prime.\n+// If it returns true, x is prime with probability 1 - 1/4^n.\n+// If it returns false, x is not prime.\n+func ProbablyPrime(x *Int, n int) bool {\n+ return !x.neg && x.abs.probablyPrime(n)\n }
// Rand sets z to a pseudo-random number in [0, n) and returns z.
@@ -671,18 +691,18 @@ func (z *Int) Rsh(x *Int, n uint) *Int {
return z
}
-// Bit returns the value of the i\'th bit of z. That is, it\n-// returns (z>>i)&1. The bit index i must be >= 0.\n-func (z *Int) Bit(i int) uint {\n+// Bit returns the value of the i\'th bit of x. That is, it\n+// returns (x>>i)&1. The bit index i must be >= 0.\n+func (x *Int) Bit(i int) uint {\n if i < 0 {\n panic("negative bit index")\n }\n-\tif z.neg {\n-\t\tt := nat(nil).sub(z.abs, natOne)\n+\tif x.neg {\n+\t\tt := nat(nil).sub(x.abs, natOne)\n \t\treturn t.bit(uint(i)) ^ 1\n \t}\n \n-\treturn z.abs.bit(uint(i))\n+\treturn x.abs.bit(uint(i))\n }\n \n // SetBit sets z to x, with x\'s i\'th bit set to b (0 or 1).\n@@ -847,11 +867,11 @@ func (z *Int) Not(x *Int) *Int {
const intGobVersion byte = 1
// GobEncode implements the gob.GobEncoder interface.
-func (z *Int) GobEncode() ([]byte, error) {\n-\tbuf := make([]byte, 1+len(z.abs)*_S) // extra byte for version and sign bit\n-\ti := z.abs.bytes(buf) - 1 // i >= 0\n+func (x *Int) GobEncode() ([]byte, error) {\n+\tbuf := make([]byte, 1+len(x.abs)*_S) // extra byte for version and sign bit\n+\ti := x.abs.bytes(buf) - 1 // i >= 0\n \tb := intGobVersion << 1 // make space for sign bit\n-\tif z.neg {\n+\tif x.neg {\n \t\tb |= 1\n \t}\n \tbuf[i] = b
コアとなるコードの解説
Bits()
とSetBits()
の追加
-
Bits()
: このメソッドは、Int
型の内部表現であるx.abs
(絶対値を表す[]Word
スライス)を直接返します。これにより、Int
の内部データに直接アクセスし、低レベルのビット操作や、math/big
パッケージが提供しない特定のアルゴリズムを外部で実装することが可能になります。例えば、特定のビットパターンを検索したり、カスタムのビットシフト操作を行ったりする際に利用できます。ただし、返されたスライスを直接変更すると、元のInt
オブジェクトの状態が意図せず変更される可能性があるため、細心の注意が必要です。 -
SetBits()
: このメソッドは、与えられたWord
スライスabs
をInt
型の新しい絶対値として設定します。nat(abs).norm()
という呼び出しは、入力スライスをnat
型(内部的な数値表現)に変換し、末尾のゼロワードを削除して正規化する役割を果たします。これにより、効率的かつ正確にInt
の値を設定できます。このメソッドも、渡されたスライスが内部配列として共有される可能性があるため、使用には注意が必要です。
これらのメソッドは、math/big
パッケージの設計哲学である「効率を損なうことなく、不足しているIntの機能を外部で実装することを可能にする」という目標を達成するためのものです。
レシーバ命名規則の修正
Bytes()
, BitLen()
, ProbablyPrime()
, Bit()
, GobEncode()
といったメソッドのレシーバ名がz
からx
に変更されました。
- 変更前(例:
func (z *Int) Bytes() []byte
): 以前は、これらのメソッドでもレシーバ名にz
が使われていました。 - 変更後(例:
func (x *Int) Bytes() []byte
): 変更後は、レシーバ名がx
になっています。
この変更は、Go言語の慣習に従い、メソッドがレシーバの値を変更しない場合にx
を使用し、レシーバの値を変更してそれを戻り値として返す場合にz
を使用するというルールを徹底するためのものです。これにより、コードの可読性と一貫性が向上します。
関連リンク
- GitHubコミットページ: https://github.com/golang/go/commit/71c19b610f1f5c0d61cc339d122eba730fc78c71
- Go CL (Change List): https://golang.org/cl/5607055
参考にした情報源リンク
- Go言語
math/big
パッケージドキュメント: https://pkg.go.dev/math/big - Go言語のレシーバ命名規則に関する一般的な情報 (例: Go Code Review Comments): https://go.dev/doc/effective_go#receivers
- Go言語の
Word
型に関する情報 (Goのソースコードや関連ドキュメント): https://go.dev/src/math/big/arith.go (内部実装の詳細) - リトルエンディアン/ビッグエンディアンに関する一般的な情報 (例: Wikipedia): https://ja.wikipedia.org/wiki/%E3%82%A8%E3%83%B3%E3%83%87%E3%82%A3%E3%82%A2%E3%83%B3