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

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

このコミットは、Go言語の初期開発段階(2008年12月)において、bignum(多倍長整数)ライブラリのテストが、その「出力(printing)」に関連する問題で失敗していたことに対する一時的な修正を記録しています。具体的には、src/lib/bignum_test.go 内の TestNatConv 関数の一部が、問題解決までの間、return;//BUG というコメント付きでスキップされるように変更されました。これは、テストの継続的な実行を可能にしつつ、根本的な問題(多倍長整数の文字列変換やフォーマットに関するバグ)が認識されていることを示すための暫定措置です。

コミット

commit 4991f20b3f9f59d3bb423a33c550a09d079d3856
Author: Ken Thompson <ken@golang.org>
Date:   Tue Dec 30 14:03:54 2008 -0800

    one bignum test fails
    has to do with printing
    patched it out
    
    R=r
    OCL=21921
    CL=21923
---
 src/lib/bignum_test.go | 2 ++\n 1 file changed, 2 insertions(+)

diff --git a/src/lib/bignum_test.go b/src/lib/bignum_test.go
index 510096c5ef..f27ec752be 100644
--- a/src/lib/bignum_test.go
+++ b/src/lib/bignum_test.go
@@ -115,6 +115,8 @@ export func TestNatConv(t *testing.T) {
 		NAT_EQ(base, NatFromString(tmp.ToString(base), base, nil), tmp);\n 	}\n \n+return;//BUG\n+\n 	test_msg = "NatConvD";\n 	x := bignum.Nat(100);\n 	y, b := bignum.NatFromString(fmt.sprintf("%b", &x), 2, nil);\

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

https://github.com/golang/go/commit/4991f20b3f9f59d3bb423a33c550a09d079d3856

元コミット内容

one bignum test fails
has to do with printing
patched it out

R=r
OCL=21921
CL=21923

変更の背景

このコミットは、Go言語の非常に初期の段階で発生した、bignum(多倍長整数)ライブラリのテスト失敗に対応するためのものです。コミットメッセージによると、テストの失敗は「printing」(出力、つまり多倍長整数の文字列変換やフォーマット)に関連していました。

当時のGo言語はまだ開発途上にあり、多くのコンポーネントが活発に構築・修正されていました。bignumのような数値計算ライブラリは、正確性とパフォーマンスが非常に重要であり、そのテストは厳密である必要があります。しかし、多倍長整数の値を様々な基数(例:2進数、10進数、16進数)で文字列に変換する処理は、特にエッジケースや非常に大きな数値において、複雑でバグが発生しやすい領域です。

テストが失敗している状態では、CI/CDパイプライン(もし当時存在していれば)や開発者のローカル環境でのテスト実行が妨げられ、他の開発作業の進行に影響を与える可能性があります。そのため、このコミットは、根本的なバグ修正に時間をかける代わりに、一時的に問題のあるテストセクションをスキップすることで、テストスイート全体がパスする状態を維持し、他の開発者が作業を続けられるようにするための「パッチアウト」(一時的な無効化)措置として行われました。//BUGというコメントは、このスキップが一時的なものであり、将来的に修正が必要な既知のバグであることを明確に示しています。

前提知識の解説

Go言語の初期開発

このコミットは2008年12月に行われており、Go言語が一般に公開される前の非常に初期の段階です。Go言語は、GoogleのRobert Griesemer、Rob Pike、Ken Thompsonによって設計されました。Ken ThompsonはUnixの共同開発者であり、Go言語の設計と実装において中心的な役割を担っていました。この時期のコードベースは、現在のGo言語とは異なる部分が多く、例えば、export func のようなキーワードや、fmt.sprintf のような関数呼び出しの構文にその名残が見られます。

多倍長整数 (bignum)

「多倍長整数」(bignum、またはarbitrary-precision arithmetic)とは、コンピュータの標準的なデータ型(例:64ビット整数)で表現できる範囲を超える、任意の大きさの整数を扱うためのデータ構造とアルゴリズムの集合です。通常の整数型ではオーバーフローが発生するような非常に大きな数値を扱う際に必要となります。暗号化、科学技術計算、金融アプリケーションなどで広く利用されます。

Go言語には、標準ライブラリとして math/big パッケージがあり、IntRatFloat などの型で多倍長整数、有理数、浮動小数点数をサポートしています。このコミットで言及されている bignum は、math/big パッケージの前身、またはその初期の実装の一部であったと考えられます。

多倍長整数の実装では、数値を配列やスライスとして表現し、各要素が数値の一部(例:基数10^9の桁)を保持します。加算、減算、乗算、除算などの基本的な算術演算は、これらの配列を操作する形で実装されます。

数値の文字列変換 (Printing/Formatting)

多倍長整数を人間が読める形式(文字列)に変換する処理は、「printing」または「formatting」と呼ばれます。これは、数値を特定の基数(例:10進数、2進数、16進数)で表現し、その桁を文字列として出力するプロセスです。

例えば、fmt.sprintf("%b", &x) は、Go言語の fmt パッケージ(C言語の sprintf に相当)を使用して、変数 x の値を2進数(%b)形式の文字列に変換しようとしています。多倍長整数の場合、この変換は特に複雑になります。

  • 基数変換: 10進数から2進数、またはその逆の変換は、繰り返し除算や乗算を行うことで実現されます。多倍長整数では、これらの操作も多倍長演算として実装する必要があります。
  • 効率: 非常に大きな数値を変換する場合、効率的なアルゴリズムが求められます。
  • 正確性: 変換結果が元の数値と完全に一致することが保証されなければなりません。
  • フォーマット: 符号、小数点、桁区切り、パディングなど、出力形式に関する様々な要件に対応する必要があります。

このコミットの背景にある問題は、おそらく bignum の内部表現から文字列への変換ロジックにバグがあったか、または特定の大きな数値や基数での変換が期待通りに機能していなかったことを示唆しています。

技術的詳細

このコミットの技術的詳細は、bignum ライブラリの TestNatConv 関数におけるテスト失敗と、それに対する一時的な回避策に集約されます。

TestNatConv 関数は、多倍長整数(Nat型)の文字列変換(ToString)と、その逆の文字列から多倍長整数への変換(NatFromString)の正確性を検証するためのテストです。テストコードの関連部分を再掲します。

@@ -115,6 +115,8 @@ export func TestNatConv(t *testing.T) {
 		NAT_EQ(base, NatFromString(tmp.ToString(base), base, nil), tmp);\n 	}\n \n+return;//BUG\n+\n 	test_msg = "NatConvD";\n 	x := bignum.Nat(100);\n 	y, b := bignum.NatFromString(fmt.sprintf("%b", &x), 2, nil);\

変更前のコードでは、return;//BUG の行は存在せず、test_msg = "NatConvD"; から始まる後続のテストロジックが実行されていました。この後続のロジックは、bignum.Nat(100) という小さな多倍長整数 x を作成し、それを fmt.sprintf("%b", &x) を使って2進数文字列に変換し、さらにその文字列を bignum.NatFromString で元の多倍長整数に戻すという一連の処理を行っています。そして、変換が正しく行われたかを検証する目的があったと推測されます。

コミットメッセージの「has to do with printing」という記述から、この fmt.sprintf("%b", &x) の部分、または tmp.ToString(base) の部分で問題が発生していた可能性が高いです。考えられる問題点としては以下が挙げられます。

  1. ToString メソッドのバグ: bignum.Nat 型の ToString(base) メソッドが、特定の基数や数値に対して誤った文字列を生成していた。
  2. NatFromString メソッドのバグ: NatFromString が、ToString によって生成された(あるいは正しく生成されたはずの)文字列を正しくパースして Nat 型に戻せていなかった。
  3. fmt.sprintfbignum の連携問題: fmt.sprintfbignum.Nat 型を2進数文字列に変換する際に、期待される動作をしなかった、または bignum 型が fmt パッケージのフォーマッターインターフェースを正しく実装していなかった。特に、Go言語の初期段階では、型とフォーマッターの連携がまだ成熟していなかった可能性があります。
  4. テストロジックの誤り: テスト自体のロジックに誤りがあり、実際にはバグがないにもかかわらずテストが失敗していた。ただし、コミットメッセージが「one bignum test fails has to do with printing」と明確にバグを示唆しているため、この可能性は低いでしょう。

Ken Thompsonは、この問題の根本原因を特定し修正する代わりに、一時的に return;//BUG を挿入することで、TestNatConv 関数がその後の問題のあるテストセクションを実行しないようにしました。これにより、テストスイート全体がパスするようになり、他の開発者がテスト失敗に悩まされることなく作業を継続できるようになります。//BUG コメントは、このコードが一時的な回避策であり、将来的に修正が必要な既知のバグが存在することを示す標準的なプラクティスです。

この種の「パッチアウト」は、大規模なプロジェクトや活発な開発が行われている環境で、クリティカルではないが修正に時間がかかるバグに対してよく用いられる手法です。これにより、開発のフローを止めずに、バグ修正を後回しにすることができます。

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

変更は src/lib/bignum_test.go ファイルの1箇所のみです。

--- a/src/lib/bignum_test.go
+++ b/src/lib/bignum_test.go
@@ -115,6 +115,8 @@ export func TestNatConv(t *testing.T) {
 		NAT_EQ(base, NatFromString(tmp.ToString(base), base, nil), tmp);\n 	}\n \n+return;//BUG\n+\n 	test_msg = "NatConvD";\n 	x := bignum.Nat(100);\n 	y, b := bignum.NatFromString(fmt.sprintf("%b", &x), 2, nil);\

具体的には、TestNatConv 関数内の既存のループ(for _, base := range bases)の直後に、以下の2行が追加されました。

return;//BUG

コアとなるコードの解説

追加された return;//BUG という行は、Go言語のテスト関数 TestNatConv の実行フローを制御します。

  • return: このキーワードは、現在の関数(TestNatConv)の実行を直ちに終了させます。これにより、return 文以降に記述されているすべてのコードは実行されなくなります。
  • //BUG: これはGo言語の単一行コメントです。BUG という文字列は、開発者がコードベース内で既知のバグや未解決の問題をマークするためによく使用する慣習的なコメントです。Go言語のツールチェーン(例えば go vetgo doc)は、BUG コメントを特別に扱うことがあります。この場合、このコメントは「ここにバグがあり、この return はそのバグを回避するための一時的な措置である」ということを明確に示しています。

したがって、この変更の目的は、TestNatConv 関数内の、多倍長整数の「printing」に関連して失敗していた特定のテストセクションを、一時的に実行しないようにすることです。これにより、テストスイート全体がパスするようになり、CIシステムや開発者のローカル環境でのテスト実行が妨げられなくなります。これは、バグの根本的な修正に時間がかかる場合に、開発の継続性を確保するための一般的な手法です。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント (pkg.go.dev)
  • GitHubのGo言語リポジトリ
  • 一般的なソフトウェア開発における「パッチアウト」や「一時的な回避策」に関する知識
  • 多倍長整数演算に関する一般的な知識
  • fmt パッケージのフォーマット動詞に関する知識