[インデックス 1768] ファイルの概要
このコミットは、Go言語の標準ライブラリの一部である src/lib/rand.go
ファイルに対する変更です。このファイルは、Go言語における擬似乱数生成機能を提供しており、整数型や浮動小数点数型の乱数を生成するための基本的な関数群を含んでいます。コミットの主な目的は、これらの乱数生成関数のドキュメンテーションを改善し、APIの振る舞いをより明確にすることにあります。
コミット
commit 6b8ac0a9e40668402d7b5f692b6e41cfa290a5b4
Author: Rob Pike <r@golang.org>
Date: Thu Mar 5 19:26:27 2009 -0800
document rand
R=rsc
DELTA=27 (16 added, 8 deleted, 3 changed)
OCL=25804
CL=25813
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/6b8ac0a9e40668402d7b5f692b6e41cfa290a5b4
元コミット内容
document rand
変更の背景
このコミットが行われた2009年3月は、Go言語がまだ一般に公開される前の初期開発段階にありました。当時のGo言語のコードベースは急速に進化しており、機能の実装と並行して、コードの可読性、保守性、そしてAPIの使いやすさを向上させるためのドキュメンテーション作業が非常に重要でした。
rand
パッケージは、プログラム内で乱数を必要とする多くの場面で利用される基本的な機能です。しかし、初期の実装では、各関数の具体的な振る舞い、特に返される値の範囲や特性に関する説明が不足していました。例えば、浮動小数点数乱数が [0.0, 1.0)
の範囲で生成されるべきであるという標準的な慣例があるにもかかわらず、その保証がコード上もドキュメント上も不明瞭な状態でした。
このコミットは、Go言語の設計思想である「明確さ」と「使いやすさ」を反映し、rand
パッケージのAPIをより理解しやすく、安全に利用できるようにするために、以下の点を目的としています。
- APIの明確化: 各乱数生成関数の目的、引数、戻り値、およびその範囲を明確に記述することで、開発者が迷うことなく適切な関数を選択できるようにする。
- コードとドキュメントの一貫性: 浮動小数点数乱数の生成範囲に関する業界標準の慣例 (
[0.0, 1.0)
) を、コードの実装とドキュメントの両方で厳密に保証する。 - コードベースの品質向上: Go言語の標準的なドキュメンテーション規約に準拠し、将来的なメンテナンスや機能拡張を容易にする。
前提知識の解説
擬似乱数生成器 (PRNG: Pseudo-Random Number Generator)
コンピュータで生成される乱数は、実際には完全にランダムではありません。これらは「擬似乱数」と呼ばれ、特定のアルゴリズム(決定論的な計算手順)に基づいて生成されます。同じ初期値(シード値)を与えれば、常に同じ数列が生成されます。
- シード (Seed): 擬似乱数生成器の初期状態を決定する値です。シードが異なれば、異なる乱数列が生成されます。再現性が必要な場合(例: シミュレーション、テスト)は固定シードを使用し、予測不可能性が必要な場合(例: セキュリティ関連)はエントロピー源(システム時間、ハードウェアノイズなど)からシードを生成します。
- 乱数の範囲: 乱数生成関数は、特定の範囲内の値を返します。例えば、
[0.0, 1.0)
は「0.0以上、1.0未満」の範囲を意味し、[0, N)
は「0以上、N未満」の整数を意味します。この範囲の厳密な定義は、統計的な特性やアプリケーションでの利用において非常に重要です。
Go言語のパッケージとドキュメンテーション規約
Go言語では、エクスポートされる(大文字で始まる)関数、変数、定数、型、フィールドには、その直前にコメントを記述することでドキュメンテーションを提供します。これらのコメントは go doc
コマンドやGoの公式ドキュメントサイトで参照可能になります。
- パッケージコメント:
package
宣言の直前に記述されるコメントは、パッケージ全体の概要を説明します。 - 関数コメント: 関数の直前に記述され、その関数の目的、引数、戻り値などを説明します。
ビット演算と数値表現
1 << N
: これはビットシフト演算子で、「1をNビット左にシフトする」ことを意味します。結果は2^N
となります。例えば、1 << 63
は2^63
を表します。int64
とuint32
: Go言語における整数型です。int64
は64ビット符号付き整数、uint32
は32ビット符号なし整数です。- 浮動小数点数:
float32
(単精度) とfloat64
(倍精度) があります。これらは実数を近似的に表現します。
技術的詳細
このコミットの技術的な変更点は、主に src/lib/rand.go
ファイル内のドキュメンテーションの追加と、浮動小数点数乱数生成の精度に関する微調整です。
-
パッケージコメントの追加:
package rand
の直前に// Uniformly distributed pseudo-random numbers.
というコメントが追加されました。これにより、このパッケージが「一様分布の擬似乱数」を提供するものであることが明確に示されます。 -
関数シグネチャリストの削除と個別コメントへの移行: 変更前は、パッケージコメントの代わりに、
rand, rand31, Int63 - return non-negative random int, int32, int64
のように、パッケージ内の主要な関数とその簡単な説明を列挙したコメントブロックがありました。このコミットでは、このリストが削除され、代わりに各関数の定義の直前に、その関数固有のドキュメンテーションコメントが追加されました。これはGo言語の標準的なドキュメンテーション規約に沿ったものであり、go doc
コマンドで個々の関数の説明を直接参照できるようになります。 -
定数
_MASK
と_MAX
の定義変更とFloat64
関数への影響: これは最も重要な技術的変更点です。- 変更前:
const _MASK = (1<<63)-1;
_MASK
は2^63 - 1
、つまり63ビット符号なし整数の最大値を表していました。Int63()
関数は[0, 2^63 - 1]
の範囲の63ビット非負整数を返します。 - 変更後:
const ( _MAX = 1<<63; _MASK = _MAX-1; )
_MAX
が2^63
と定義され、_MASK
は_MAX - 1
、つまり2^63 - 1
となりました。_MASK
の値自体は変更されていませんが、_MAX
という新しい定数が導入されました。
この
_MAX
の導入は、Float64()
関数の実装に影響を与えます。- 変更前:
x := float64(Int63()) / float64(_MASK);
Int63()
が返す最大値(2^63 - 1)
を_MASK
(2^63 - 1
) で割ると、結果は1.0
になります。これは、Float64()
が[0.0, 1.0]
の範囲の乱数を生成する可能性があることを意味します。 - 変更後:
x := float64(Int63()) / float64(_MAX);
Int63()
が返す最大値(2^63 - 1)
を_MAX
(2^63
) で割ると、結果は(2^63 - 1) / 2^63
となり、これは常に1.0
より厳密に小さくなります。これにより、Float64()
関数は[0.0, 1.0)
(0.0を含み、1.0を含まない) という、浮動小数点数乱数生成の標準的な慣例に沿った範囲の値を生成することが保証されます。for x >= 1
ループは、浮動小数点数の精度誤差によって稀に1.0
になるケースを考慮して残されていますが、_MAX
で割ることでその発生確率は大幅に減少します。
- 変更前:
これらの変更は、Go言語の乱数生成APIの使いやすさと正確性を向上させるための重要なステップでした。
コアとなるコードの変更箇所
src/lib/rand.go
--- a/src/lib/rand.go
+++ b/src/lib/rand.go
@@ -2,24 +2,19 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// Uniformly distributed pseudo-random numbers.
+package rand
/*
*\talgorithm by
*\tDP Mitchell and JA Reeds
*/
-package rand
-
-// rand, rand31, Int63 - return non-negative random int, int32, int64
-// urand32 - return random uint32
-// nrand, nrand31, Int63n - return 0 <= random < n
-// frand, frand64, frand32 - return 0 <= random float, float64, float32 < 1
-// perm gives a random permutation []int
-
const (
_LEN = 607;
_TAP = 273;
-\t_MASK = (1<<63)-1;
+\t_MAX = 1<<63;
+\t_MASK = _MAX-1;
_A = 48271;
_M = 2147483647;
_Q = 44488;
@@ -44,6 +39,7 @@ func seedrand(x int32) int32 {\n \treturn x;\n }\n \n+// Seed uses the provided seed value to initialize the generator to a deterministic state.\n func Seed(seed int32) {\n \trng_tap = 0;\n \trng_feed = _LEN-_TAP;\n@@ -72,6 +68,7 @@ func Seed(seed int32) {\n \t}\n }\n \n+// Int63 returns a non-negative pseudo-random 63-bit integer as an int64.\n func Int63() int64 {\n \trng_tap--;\n \tif rng_tap < 0 {\n@@ -88,19 +85,23 @@ func Int63() int64 {\n \treturn x;\n }\n \n+// Uint32 returns a pseudo-random 32-bit value as a uint32.\n func Uint32() uint32 {\n \treturn uint32(Int63() >> 31);\n }\n \n+// Int31 returns a non-negative pseudo-random 31-bit integer as an int32.\n func Int31() int32 {\n \treturn int32(Int63() >> 32);\n }\n \n+// Int returns a non-negative pseudo-random int. All bits but the top bit are random.\n func Int() int {\n \tu := uint(Int63());\n \treturn int(u << 1 >> 1);\t// clear sign bit if int == int32\n }\n \n+// Int63n returns, as an int64, a non-negative pseudo-random number in [0,n).\n func Int63n(n int64) int64 {\n \tif n <= 0 {\n \t\treturn 0\n@@ -113,31 +114,37 @@ func Int63n(n int64) int64 {\n \treturn v % n\n }\n \n+// Int31n returns, as an int32, a non-negative pseudo-random number in [0,n).\n func Int31n(n int32) int32 {\n \treturn int32(Int63n(int64(n)))\n }\n \n+// Intn returns, as an int, a non-negative pseudo-random number in [0,n).\n func Intn(n int) int {\n \treturn int(Int63n(int64(n)))\n }\n \n+// Float64 returns, as a float64, a pseudo-random number in [0.0,1.0).\n func Float64() float64 {\n-\tx := float64(Int63()) / float64(_MASK);\n+\tx := float64(Int63()) / float64(_MAX);\n \tfor x >= 1 {\n-\t\tx = float64(Int63()) / float64(_MASK);\n+\t\tx = float64(Int63()) / float64(_MAX);\n \t}\n \treturn x;\n }\n \n+// Float32 returns, as a float32, a pseudo-random number in [0.0,1.0).\n func Float32() float32 {\n \treturn float32(Float64())\n }\n \n+// Float returns, as a float, a pseudo-random number in [0.0,1.0).\n func Float() float\n {\n \treturn float(Float64())\n }\n \n+// Perm returns, as an array of n ints, a pseudo-random permutation of the integers [0,n).\n func Perm(n int) []int {\n \tm := make([]int, n);\n \tfor i:=0; i<n; i++ {\n```
## コアとなるコードの解説
このコミットにおける主要なコード変更は以下の通りです。
1. **パッケージ宣言の直前へのコメント追加**:
`package rand` の行の直前に `// Uniformly distributed pseudo-random numbers.` というコメントが追加されました。これはGo言語のドキュメンテーション規約に従い、パッケージ全体の目的を簡潔に説明するものです。これにより、`go doc rand` コマンドを実行した際に、このパッケージが提供する機能の概要がすぐに理解できるようになります。
2. **定数定義の変更**:
`_MASK` の定義が `_MAX` を用いる形に変更されました。
* `_MAX = 1<<63;` は、`2` の `63` 乗、すなわち `9,223,372,036,854,775,808` を表します。これは、64ビット符号付き整数 `int64` で表現できる正の最大値 `(2^63 - 1)` よりも1大きい値です。
* `_MASK = _MAX-1;` は、`_MAX` から1を引いた値、すなわち `2^63 - 1` を表します。これは `Int63()` 関数が生成する63ビット非負整数の最大値と一致します。
この変更は、`_MASK` の意味をより明確にするためのものであり、特に `Float64()` 関数での利用においてその意図が明確になります。
3. **`Float64()` 関数の除算対象の変更**:
`Float64()` 関数内で、`Int63()` の結果を割る対象が `_MASK` から `_MAX` に変更されました。
* 変更前: `float64(Int63()) / float64(_MASK)`
`Int63()` が返す値は `[0, 2^63 - 1]` の範囲です。これを `_MASK` (`2^63 - 1`) で割ると、最大値 `(2^63 - 1)` が `(2^63 - 1)` で割られ、結果が `1.0` になる可能性があります。これにより、`Float64()` の出力範囲が `[0.0, 1.0]` となり、`1.0` を含む可能性がありました。
* 変更後: `float64(Int63()) / float64(_MAX)`
`Int63()` が返す最大値 `(2^63 - 1)` を `_MAX` (`2^63`) で割ると、結果は `(2^63 - 1) / 2^63` となり、これは常に `1.0` より厳密に小さくなります。これにより、`Float64()` の出力範囲が `[0.0, 1.0)` (0.0を含み、1.0を含まない) であることが数学的に保証されます。これは、多くのプログラミング言語や統計ライブラリにおける浮動小数点数乱数の標準的な振る舞いです。`for x >= 1` ループは、浮動小数点演算の丸め誤差などによって稀に `1.0` になるケースを捕捉するための安全策として残されています。
4. **各乱数生成関数への詳細なコメント追加**:
`Seed`, `Int63`, `Uint32`, `Int31`, `Int`, `Int63n`, `Int31n`, `Intn`, `Float64`, `Float32`, `Float`, `Perm` といった主要な乱数生成関数それぞれに、その機能、戻り値の型、および生成される乱数の範囲を明確に記述したコメントが追加されました。これにより、これらの関数が何を行い、どのような値を返すのかが、コードを読むだけで容易に理解できるようになりました。
これらの変更は、Go言語の `rand` パッケージのAPIをより堅牢で、予測可能で、使いやすいものにするための基盤を築きました。
## 関連リンク
* Go言語 `math/rand` パッケージの現在のドキュメント: [https://pkg.go.dev/math/rand](https://pkg.go.dev/math/rand)
(このコミットの `src/lib/rand.go` は、後の `math/rand` パッケージの原型の一つです。)
## 参考にした情報源リンク
* Go言語のドキュメンテーション規約: [https://go.dev/doc/effective_go#commentary](https://go.dev/doc/effective_go#commentary)
* 擬似乱数生成器 (Wikipedia): [https://ja.wikipedia.org/wiki/%E6%93%AC%E4%BC%BC%E4%B9%B1%E6%95%B0%E7%94%9F%E6%88%90%E5%B8%83](https://ja.wikipedia.org/wiki/%E6%93%AC%E4%BC%BC%E4%B9%B1%E6%95%B0%E7%94%9F%E6%88%90%E5%B8%83)
* 浮動小数点数 (Wikipedia): [https://ja.wikipedia.org/wiki/%E6%B5%AE%E5%8B%95%E5%B0%8F%E6%95%B0%E7%82%B9%E6%95%B0](https://ja.wikipedia.org/wiki/%E6%B5%AE%E5%8B%95%E5%B0%8F%E6%95%B0%E7%82%B9%E6%95%B0)
* Go言語の初期開発に関する情報 (Goブログなど): [https://go.dev/blog/](https://go.dev/blog/)