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

[インデックス 11421] ファイルの概要

このコミットは、Go言語の math/big パッケージ内の bitLen および bitLen_g というビット長計算関数のテストコードを改善するものです。具体的には、両方の関数が正しく動作することを検証するためのテストを追加し、既存のテストコードをより簡潔で直接的なものに修正しています。

コミット

commit 8a90a8861f66525fed7f4b5e8d5499af0248806a
Author: Robert Griesemer <gri@golang.org>
Date:   Thu Jan 26 10:08:21 2012 -0800

    math/big: test both bitLen and bitLen_g
    
    Also: simpler, more direct test.
    
    R=golang-dev, dave.andersen
    CC=golang-dev
    https://golang.org/cl/5573070

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/8a90a8861f66525fed7f4b5e8d5499af0248806a

元コミット内容

このコミットの目的は、math/big パッケージにおけるビット長を計算する関数 bitLenbitLen_g の両方をテストすることです。また、既存のテストコードをよりシンプルで直接的なアプローチに改善することも意図されています。

変更の背景

math/big パッケージは、任意精度の算術演算を提供するGo言語の標準ライブラリです。このようなパッケージでは、数値のビット長を正確に計算する機能は非常に重要であり、その実装はパフォーマンスと正確性の両面で厳密なテストが求められます。

bitLen は、big.Int 型の絶対値を表現するのに必要な最小ビット数を返す公開されたメソッドです。一方、bitLen_g は、math/big パッケージの公開APIの一部ではない、内部的またはエクスポートされていないヘルパー関数である可能性が高いです。これは、特定のプラットフォーム向けの実装や、過去のバージョンのビット長計算ロジックを指している可能性があります。

このコミットが行われた2012年当時、Go言語はまだ発展途上にあり、標準ライブラリのコードベースも継続的に改善されていました。このような状況下で、異なる実装(bitLenbitLen_g)が存在する場合、それら両方が同じ期待される動作をすることを保証するための包括的なテストが不可欠となります。

既存のテストコードは、おそらく bitLen のみを対象としていたか、あるいはそのテストロジックが冗長であったため、より効率的で網羅的なテストフレームワークを導入する必要がありました。この変更は、コードの品質と信頼性を向上させ、将来的なバグの混入を防ぐことを目的としています。

前提知識の解説

math/big パッケージ

math/big パッケージは、Go言語で任意精度の数値演算を行うための標準ライブラリです。通常の intuint 型では表現できない非常に大きな整数、有理数、浮動小数点数を扱うことができます。これは、暗号化、科学計算、金融アプリケーションなど、高い精度が要求される場面で不可欠です。

Word

math/big パッケージ内部では、大きな数値を Word 型のスライスとして扱います。Word は通常、uint または uint64 のエイリアスであり、システムが一度に処理できる最大の符号なし整数を表します。ビット長計算は、この Word 型の値を対象に行われます。

ビット長 (Bit Length)

数値のビット長とは、その数値を表現するために必要な最小のビット数です。例えば:

  • 0 のビット長は 0
  • 1 のビット長は 1 (バイナリで 1)
  • 2 または 3 のビット長は 2 (バイナリで 10 または 11)
  • 4 から 7 のビット長は 3 (バイナリで 100 から 111)

一般的に、正の整数 x のビット長は floor(log2(x)) + 1 で計算されます。0 の場合は特別に 0 と定義されます。

bitLenbitLen_g

  • bitLen: math/big パッケージの公開APIである big.Int.BitLen() メソッドの基盤となる関数です。これは、与えられた Word のビット長を計算します。
  • bitLen_g: これは math/big パッケージの公開APIには含まれていない、内部的な(unexported)ヘルパー関数です。Go言語では、関数名が小文字で始まる場合、その関数はパッケージ外からはアクセスできません。_g サフィックスは、"generic"(汎用)や"go"(Go言語で書かれた)の実装を示す慣習的なものかもしれません。これは、特定のアーキテクチャに最適化されたアセンブリ実装など、他の bitLen のバリアントが存在する可能性を示唆しています。このコミットの目的は、この内部実装も正しく機能することを保証することにあります。

技術的詳細

このコミットの主要な技術的変更は、arith_test.go ファイル内の TestWordBitLen 関数をリファクタリングし、bitLenbitLen_g の両方をテストするための汎用的なヘルパー関数 testWordBitLen を導入した点です。

元の TestWordBitLen 関数は、bitLen のみをテストしており、xy という2つの異なるパターン(...00010000......00011111...)でビット長を検証していました。このアプローチは、各ビット位置での bitLen の動作を確認するには有効でしたが、コードがやや冗長であり、新しい関数 bitLen_g を追加でテストするには、同様のロジックを複製する必要がありました。

新しい testWordBitLen 関数は、テスト対象の関数(bitLen または bitLen_g)を引数として受け取ることで、コードの再利用性を高めています。このヘルパー関数は、0 から _W (Wordのビット幅、通常は32または64) までの各ビット位置 i に対して、Word(1) << uint(i-1) という形式のテスト値 x を生成します。

  • i == 0 の場合、x0 となります。bitLen(0)0 を返すことが期待されます。
  • i > 0 の場合、x1i-1 ビット左シフトした値、つまり 2^(i-1) となります。この値のビット長は i であることが期待されます。例えば、i=1 なら x=1 (ビット長1)、i=2 なら x=2 (ビット長2)、i=3 なら x=4 (ビット長3) となります。

このテストパターンは、各ビット位置がセットされた単一のビットを持つ数値のビット長を正確に検証します。これにより、bitLen および bitLen_g が、それぞれのビット位置で正しくビット長を計算できるかを網羅的に確認できます。

TestWordBitLen 関数自体は、testWordBitLenbitLenbitLen_g の両方に呼び出すことで、両方の実装が同じテストスイートを通過することを保証します。これにより、将来的にどちらかの実装が変更された場合でも、もう一方の実装との整合性が保たれているかを容易に確認できるようになります。

コアとなるコードの変更箇所

diff --git a/src/pkg/math/big/arith_test.go b/src/pkg/math/big/arith_test.go
index cd02ba3674..c7e3d284c2 100644
--- a/src/pkg/math/big/arith_test.go
+++ b/src/pkg/math/big/arith_test.go
@@ -334,29 +334,21 @@ func TestMulAddWWW(t *testing.T) {\n \t}\n }\n \n-func TestWordBitLen(t *testing.T) {\n-\t// Test every possible output of bitLen with the high bit set\n-\t// and then with all bits below max set\n-\tz := bitLen(0)\n-\tif z != 0 {\n-\t\tt.Errorf(\"0 got %d want 0\", z)\n-\t}\n-\tx := Word(1) // Will be ...00010000...\n-\ty := Word(1) // Will be ...00011111...\n-\tfor i := 1; i <= _W; i++ {\n-\t\tz = bitLen(x)\n-\t\tif z != i {\n-\t\t\tt.Errorf(\"%x got %d want %d\", x, z, i)\n-\t\t}\n-\t\tz = bitLen(y)\n-\t\tif z != i {\n-\t\t\tt.Errorf(\"%x got %d want %d\", y, z, i)\n-+func testWordBitLen(t *testing.T, fname string, f func(Word) int) {\n++func testWordBitLen(t *testing.T, fname string, f func(Word) int) {\n+\tfor i := 0; i <= _W; i++ {\n+\t\tx := Word(1) << uint(i-1) // i == 0 => x == 0\n+\t\tn := f(x)\n+\t\tif n != i {\n+\t\t\tt.Errorf(\"got %d; want %d for %s(%#x)\", n, i, fname, x)\n \t\t}\n-\t\tx <<= 1\n-\t\ty = (y << 1) | 0x1\n \t}\n }\n \n+func TestWordBitLen(t *testing.T) {\n+\ttestWordBitLen(t, \"bitLen\", bitLen)\n+\ttestWordBitLen(t, \"bitLen_g\", bitLen_g)\n+}\n+\n // runs b.N iterations of bitLen called on a Word containing (1 << nbits)-1.\n func benchmarkBitLenN(b *testing.B, nbits uint) {\n \ttestword := Word((uint64(1) << nbits) - 1)\n```

## コアとなるコードの解説

変更は `src/pkg/math/big/arith_test.go` ファイルに集中しています。

1.  **`TestWordBitLen` 関数の削除と置き換え**:
    元の `TestWordBitLen` 関数は削除され、その機能は新しいヘルパー関数 `testWordBitLen` に移管されました。元の関数は `bitLen(0)` のテストと、`x` (`...00010000...` の形式) と `y` (`...00011111...` の形式) の2種類のテストパターンで `bitLen` をループでテストしていました。このロジックは、新しい汎用的なテスト関数によって置き換えられました。

2.  **新しいヘルパー関数 `testWordBitLen` の導入**:
    ```go
    func testWordBitLen(t *testing.T, fname string, f func(Word) int) {
    	for i := 0; i <= _W; i++ {
    		x := Word(1) << uint(i-1) // i == 0 => x == 0
    		n := f(x)
    		if n != i {
    			t.Errorf("got %d; want %d for %s(%#x)", n, i, fname, x)
    		}
    	}
    }
    ```
    *   この関数は、`*testing.T` (テストユーティリティ)、`fname` (テスト対象の関数名、エラーメッセージ用)、そして `f func(Word) int` (テスト対象のビット長計算関数自体) を引数として受け取ります。
    *   `for i := 0; i <= _W; i++` ループは、`0` から `Word` のビット幅 (`_W`) までのすべての可能なビット長をテストします。
    *   `x := Word(1) << uint(i-1)`: この行がテスト値を生成する核心です。
        *   `i == 0` の場合: `uint(i-1)` は `uint(-1)` となり、Goの仕様ではこれは非常に大きな符号なし整数になりますが、結果的に `Word(1) << uint(-1)` は `0` になります(シフト量がWordのビット幅を超えるため)。これにより、`bitLen(0)` が `0` を返すことをテストします。
        *   `i > 0` の場合: `Word(1)` を `i-1` ビット左シフトします。これにより、`2^(i-1)` という値が生成されます。例えば、`i=1` なら `1 << 0 = 1`、`i=2` なら `1 << 1 = 2`、`i=3` なら `1 << 2 = 4` となります。これらの値のビット長はそれぞれ `1`, `2`, `3` となり、期待される結果 `i` と一致します。
    *   `n := f(x)`: 引数として渡された関数 `f` (つまり `bitLen` または `bitLen_g`) を、生成されたテスト値 `x` で呼び出し、結果を `n` に格納します。
    *   `if n != i`: 期待されるビット長 `i` と、実際に計算されたビット長 `n` を比較します。一致しない場合はエラーを報告します。エラーメッセージには、テスト対象の関数名 (`fname`) とテスト値 (`x`) が含まれ、デバッグに役立ちます。

3.  **新しい `TestWordBitLen` 関数の定義**:
    ```go
    func TestWordBitLen(t *testing.T) {
    	testWordBitLen(t, "bitLen", bitLen)
    	testWordBitLen(t, "bitLen_g", bitLen_g)
    }
    ```
    *   この新しい `TestWordBitLen` 関数は、Goのテストフレームワークによって自動的に実行されます。
    *   内部で `testWordBitLen` ヘルパー関数を2回呼び出します。
        *   1回目は `bitLen` 関数をテストするために、`"bitLen"` という名前と `bitLen` 関数自体を渡します。
        *   2回目は `bitLen_g` 関数をテストするために、`"bitLen_g"` という名前と `bitLen_g` 関数自体を渡します。

この変更により、`bitLen` と `bitLen_g` の両方が、単一の簡潔で網羅的なテストロジックで検証されるようになり、テストコードの保守性と拡張性が大幅に向上しました。

## 関連リンク

*   Go CL 5573070: [https://golang.org/cl/5573070](https://golang.org/cl/5573070)

## 参考にした情報源リンク

*   Go言語 `math/big` パッケージの `BitLen()` メソッドに関するドキュメント:
    *   [https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEMyL2b1N6Aq-bDlnU1pnnv3RlWW6M_gqS8_Q2toK56vIHurNLn5128xiIJ6V_W6XVEs-2k-oD2CSDTumeowsd_EN8wzrsJ552owP7jV6kpe6etvZdmvw==](https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEMyL2b1N6Aq-bDlnU1pnnv3RlWW6M_gqS8_Q2toK56vIHurNLn5128xiIJ6V_W6XVEs-2k-oD2CSDTumeowsd_EN8wzrsJ552owP7jV6kpe6etvZdmvw==)
    *   [https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEyIItqzmXGbZAqpjngX0JsO6Yqhy1ceZBWFkyLBZ9p19l2RC7LbfN1PzV2ZJTrud5NAZovGGdrfhS6rMnff4NQG_8Y5ocgp6FPGlMRv7cwsWDThem20SejS2iD52Qk-Zu3N4TiGJuTBg3WnVkrH22d2d_Q==](https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEyIItqzmXGbZAqpjngX0JsO6Yqhy1ceZBWFkyLBZ9p19l2RC7LbfN1PzV2ZJTrud5NAZovGGdrfhS6rMnff4NQG_8Y5ocgp6FPGlMRv7cwsWDThem20SejS2iD52Qk-Zu3N4TiGJuTBg3WnVkrH22d2d_Q==)
*   `math/big` パッケージの概要:
    *   [https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQH9qV9iUxLZFMZni47XlI0naDXlxpI2kQxqDxy6BtqP9yYmCU_zuWxY0VhsvnnroR1_Te_Nb39ePGkr-wbdB6CnOTNbQUAzDcHhQBuD8oRQPKb8zdt2muB09-UWfVS9gAkkJRX4mDGDU32EEXwXdy61zqxD3cdjwstVOvwUWf_MISfELK24JIjBXg==](https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQH9qV9iUxLZFMZni47XlI0naDXlxpI2kQxqDxy6BtqP9yYmCU_zuWxY0VhsvnnroR1_Te_Nb39ePGkr-wbdB6CnOTNbQUAzDcHhQBuD8oRQPKb8zdt2muB09-UWfVS9gAkkJRX4mDGDU32EEXwXdy61zqxD3cdjwstVOvwUWf_MISfELK24JIjBXg==)
*   `big.Int.BitLen` の回帰と内部関数に関する議論 (2022年のものですが、`bitLen_g` のような内部関数の存在を示唆):
    *   [https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQG1QOUopCQPzyuVBP6LWMTTCZeL5DKqbddA4C9l5pAidMkyqbNOo2ZspSdfEO_26-t7vVUjng1F0JDBaIsVSP5plKpE-xgTeGF1JO2Zu0mzzTFa_xMCwYBSka32GYyZ2NB7QOci](https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQG1QOUopCQPzyuVBP6LWMTTCZeL5DKqbddA4C9l5pAidMkyqbNOo2ZspSdfEO_26-t7vVUjng1F0JDBaIsVSP5plKpE-xgTeGF1JO2Zu0mzzTFa_xMCwYBSka32GYyZ2NB7QOci)