Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 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をより理解しやすく、安全に利用できるようにするために、以下の点を目的としています。

  1. APIの明確化: 各乱数生成関数の目的、引数、戻り値、およびその範囲を明確に記述することで、開発者が迷うことなく適切な関数を選択できるようにする。
  2. コードとドキュメントの一貫性: 浮動小数点数乱数の生成範囲に関する業界標準の慣例 ([0.0, 1.0)) を、コードの実装とドキュメントの両方で厳密に保証する。
  3. コードベースの品質向上: 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 << 632^63 を表します。
  • int64uint32: Go言語における整数型です。int64 は64ビット符号付き整数、uint32 は32ビット符号なし整数です。
  • 浮動小数点数: float32 (単精度) と float64 (倍精度) があります。これらは実数を近似的に表現します。

技術的詳細

このコミットの技術的な変更点は、主に src/lib/rand.go ファイル内のドキュメンテーションの追加と、浮動小数点数乱数生成の精度に関する微調整です。

  1. パッケージコメントの追加: package rand の直前に // Uniformly distributed pseudo-random numbers. というコメントが追加されました。これにより、このパッケージが「一様分布の擬似乱数」を提供するものであることが明確に示されます。

  2. 関数シグネチャリストの削除と個別コメントへの移行: 変更前は、パッケージコメントの代わりに、rand, rand31, Int63 - return non-negative random int, int32, int64 のように、パッケージ内の主要な関数とその簡単な説明を列挙したコメントブロックがありました。このコミットでは、このリストが削除され、代わりに各関数の定義の直前に、その関数固有のドキュメンテーションコメントが追加されました。これはGo言語の標準的なドキュメンテーション規約に沿ったものであり、go doc コマンドで個々の関数の説明を直接参照できるようになります。

  3. 定数 _MASK_MAX の定義変更と Float64 関数への影響: これは最も重要な技術的変更点です。

    • 変更前: const _MASK = (1<<63)-1; _MASK2^63 - 1、つまり63ビット符号なし整数の最大値を表していました。Int63() 関数は [0, 2^63 - 1] の範囲の63ビット非負整数を返します。
    • 変更後:
      const (
          _MAX  = 1<<63;
          _MASK = _MAX-1;
      )
      
      _MAX2^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/)