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

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

このコミットは、Go言語の標準ライブラリ math/big パッケージにおける、いくつかの軽微なクリーンアップと修正を目的としています。具体的には、コメントの修正、(*Rat).Float64 メソッドにおける変数名の慣習に合わせた変更、およびテスト出力の微調整が含まれています。これらの変更は、コードの可読性と一貫性を向上させ、将来的なメンテナンスを容易にすることを意図しています。

コミット

commit 1b37ba931f383597c8159bb88f5733dfd54dda9d
Author: Robert Griesemer <gri@golang.org>
Date:   Wed Apr 3 13:24:32 2013 -0700

    math/big: minor cleanups
    
    - comment fixes
    - s/z/x/ in (*rat).Float64 to match convention for functions
      returning a non-*Rat
    - minor test output tweaking
    
    R=golang-dev, r
    CC=golang-dev
    https://golang.org/cl/8327044

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

https://github.com/golang/go/commit/1b37ba931f383597c8159bb88f5733dfd54dda9d

元コミット内容

math/big: minor cleanups
    
- comment fixes
- s/z/x/ in (*rat).Float64 to match convention for functions
  returning a non-*Rat
- minor test output tweaking

変更の背景

このコミットの背景には、Go言語の標準ライブラリにおけるコード品質と一貫性の維持という一般的な目標があります。math/big パッケージは、任意精度整数 (Int)、任意精度有理数 (Rat)、任意精度浮動小数点数 (Float) を扱うための重要なパッケージであり、その正確性と信頼性は非常に重要です。

このコミットで行われた変更は、主に以下の点に焦点を当てています。

  1. コメントの正確性向上: コードの意図をより正確に反映させるためのコメント修正。これは、コードの理解を深め、将来の開発者が誤解するリスクを減らすために不可欠です。
  2. 変数名の慣習への準拠: Go言語のコードベースでは、特定のパターンに従った変数名の慣習が存在します。特に、レシーバ変数(メソッドの最初の引数)の名前付けには慣習があり、このコミットでは (*Rat).Float64 メソッドにおいて、その慣習に合わせるための変更が行われました。具体的には、レシーバがポインタ型でなく、かつその型が値を返す関数である場合、レシーバ名を x とすることが推奨される場合があります。この変更は、コードベース全体の一貫性を保ち、Goコミュニティの慣習に従うことを目的としています。
  3. テスト出力の調整: テストの出力がより明確になるように微調整が行われました。これは、テスト結果の解釈を容易にし、デバッグプロセスを効率化するために重要です。

これらの変更は、機能的なバグ修正というよりも、コードの「衛生状態」を改善し、長期的な保守性を高めるためのものです。

前提知識の解説

このコミットを理解するためには、以下の前提知識が役立ちます。

1. Go言語の math/big パッケージ

math/big パッケージは、Go言語で任意精度の数値を扱うための機能を提供します。これは、標準の intfloat64 型では表現できない非常に大きな数値や、高い精度が要求される計算を行う場合に必要となります。

  • Int: 任意精度の整数型。
  • Rat: 任意精度の有理数型(分数)。分子と分母を Int 型で持ちます。
  • Float: 任意精度の浮動小数点数型。

これらの型は、金融計算、暗号化、科学技術計算など、精度が重要なアプリケーションで広く使用されます。

2. Go言語のレシーバ変数名慣習

Go言語では、メソッドのレシーバ変数(func (r ReceiverType) MethodName(...)r の部分)には特定の命名慣習があります。

  • レシーバがポインタ型 (*Type) の場合、通常は型名の最初の1文字(例: *Int なら i*Rat なら r)を使用します。
  • レシーバが値型 (Type) の場合も同様に型名の最初の1文字を使用することが多いですが、特にそのメソッドがレシーバの値を変更せず、単に値を返すような場合、xv のような一般的な変数名が使われることがあります。このコミットの (*Rat).Float64 のケースでは、Float64Rat の値を変更せず、float64 の値を返すため、レシーバ名を z から x に変更することで、この慣習に合わせようとしています。

3. 任意精度演算の内部表現

math/big パッケージの数値は、内部的には nat (natural number) 型のスライス([]Word)で表現されます。Word はプラットフォームのワードサイズ(32ビットまたは64ビット)に応じた符号なし整数型です。これにより、メモリが許す限り任意の大きさの数値を表現できます。

  • Int 型は、abs (絶対値) と neg (負の符号) のフィールドを持ちます。absnat 型で、数値の絶対値を表します。
  • Rat 型は、a (分子) と b (分母) の Int 型フィールドを持ちます。

4. 浮動小数点数 (float64) の表現

Go言語の float64 は、IEEE 754 倍精度浮動小数点数標準に準拠しています。これは、符号ビット、指数部、仮数部で構成され、非常に広い範囲の数値を表現できますが、精度には限界があります。任意精度有理数から float64 への変換は、この精度の限界内で最も近い表現を見つけるプロセスです。

技術的詳細

このコミットは、以下の3つのファイルにわたる変更を含んでいます。

  1. src/pkg/math/big/int.go
  2. src/pkg/math/big/rat.go
  3. src/pkg/math/big/rat_test.go

それぞれのファイルにおける変更点を詳しく見ていきます。

src/pkg/math/big/int.go の変更

このファイルでは、Int 型の SetUint64 メソッドと Int64 メソッドに修正が加えられています。

  • func (z *Int) SetUint64(x uint64) *Int:

    • 変更前: z.abs = z.abs.setUint64(uint64(x))
    • 変更後: z.abs = z.abs.setUint64(x)
    • uint64(x) のキャストが削除されました。x は既に uint64 型であるため、このキャストは冗長であり、削除することでコードがより簡潔になります。これは、Goの型システムにおける暗黙的な型変換のルールと、明示的なキャストが不要なケースを示しています。
  • func (x *Int) Int64() int64:

    • 変更前は、x.abs の長さに基づいて int64 の値を直接構築していました。特に、_W == 32 (ワードサイズが32ビット) の場合に、x.abs の2つの要素を組み合わせて int64 を構築するロジックが含まれていました。
    • 変更後: v := int64(x.Uint64())
    • この変更は、Int64() メソッドが Uint64() メソッドを呼び出すように簡略化されました。これにより、Int64() の実装が Uint64() のロジックに依存するようになり、コードの重複が避けられ、一貫性が向上します。Uint64() メソッドは、Intuint64 で表現可能かどうかを適切に処理し、その値を返します。Int64() はその結果を int64 にキャストし、必要に応じて符号を反転させます。
  • func (x *Int) Uint64() uint64 のコメント修正:

    • 変更前: // If x cannot be represented in an uint64, the result is undefined.
    • 変更後: // If x cannot be represented in a uint64, the result is undefined.
    • "an uint64" から "a uint64" への修正です。これは英語の冠詞の文法的な修正であり、コードの機能には影響しませんが、コメントの正確性とプロフェッショナルな印象を向上させます。

src/pkg/math/big/rat.go の変更

このファイルでは、Rat 型の Float64 メソッドに重要な変更が加えられています。

  • func (z *Rat) Float64() (f float64, exact bool):
    • レシーバ変数名の変更: z から x へ変更されました。
      • 変更前: func (z *Rat) Float64() (f float64, exact bool)
      • 変更後: func (x *Rat) Float64() (f float64, exact bool)
      • これは、コミットメッセージで言及されている「s/z/x/ in (*rat).Float64 to match convention for functions returning a non-*Rat」に該当します。Float64 メソッドは *Rat 型を返さず、float64 型と bool 型を返します。Goの慣習として、レシーバがポインタ型でなく、かつそのメソッドがレシーバの値を変更しない(またはレシーバ型を返さない)場合、レシーバ名を x とすることが推奨されることがあります。これにより、コードベース全体での命名の一貫性が保たれます。
    • レシーバ変数名変更に伴う内部参照の更新: メソッド内部で z を参照していた箇所がすべて x に変更されました。
      • b := z.b.abs -> b := x.b.abs
      • f, exact = quotToFloat(z.a.abs, b) -> f, exact = quotToFloat(x.a.abs, b)
      • if z.a.neg { -> if x.a.neg {
    • コメントの修正: メソッドのコメントがより詳細かつ正確になりました。
      • 変更前:
        // Float64 returns the nearest float64 value to z.
        // If z is exactly representable as a float64, Float64 returns exact=true.
        // If z is negative, so too is f, even if f==0.
        
      • 変更後:
        // Float64 returns the nearest float64 value for x and a bool indicating
        // whether f represents x exactly. The sign of f always matches the sign
        // of x, even if f == 0.
        
      • 新しいコメントは、返される fx を正確に表現しているかどうかを示す exact の役割を明確にし、f の符号が x の符号と常に一致すること(f == 0 の場合でも)を強調しています。これは、浮動小数点数のゼロが正と負の両方の表現を持つ可能性があるため、特に重要な詳細です。

src/pkg/math/big/rat_test.go の変更

このファイルでは、テストコードのコメントと出力フォーマットに微調整が加えられています。

  • コメントの削除:

    • var float64inputs = []string{ の直後にあった空行とコメント行 // および // Constants plundered from strconv/testfp.txt. が削除されました。
    • 同様に、var float64inputs = []string{ の別の箇所でも、// および // Constants plundered from strconv/atof_test.go. が削除されました。
    • これらのコメントは、テストデータの出所を示すものでしたが、コードの簡潔性を保つために削除された可能性があります。
  • テスト出力フォーマットの調整: t.Errorf メッセージ内のスペースが調整されました。

    • delta=%g -> delta = %g
    • f=%g (%b); f~%g; r=%v -> f = %g (%b); f ~ %g; r = %v
    • delta=%b -> delta = %b
    • これらの変更は、テスト失敗時のエラーメッセージの可読性を向上させるためのものです。等号 (=) やチルダ (~) の周りにスペースを追加することで、出力がより整然と見えます。これは、デバッグ時にエラーメッセージを素早く解析するのに役立ちます。

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

src/pkg/math/big/int.go

--- a/src/pkg/math/big/int.go
+++ b/src/pkg/math/big/int.go
@@ -53,7 +53,7 @@ func (z *Int) SetInt64(x int64) *Int {
 
 // SetUint64 sets z to x and returns z.
 func (z *Int) SetUint64(x uint64) *Int {
-	z.abs = z.abs.setUint64(uint64(x))
+	z.abs = z.abs.setUint64(x)
 	z.neg = false
 	return z
 }
@@ -513,13 +513,7 @@ func (z *Int) Scan(s fmt.ScanState, ch rune) error {
 // Int64 returns the int64 representation of x.
 // If x cannot be represented in an int64, the result is undefined.
 func (x *Int) Int64() int64 {
-	if len(x.abs) == 0 {
-		return 0
-	}
-	v := int64(x.abs[0])
-	if _W == 32 && len(x.abs) > 1 {
-		v |= int64(x.abs[1]) << 32
-	}
+	v := int64(x.Uint64())
 	if x.neg {
 		v = -v
 	}
@@ -527,7 +521,7 @@ func (x *Int) Int64() int64 {
 }
 
 // Uint64 returns the uint64 representation of x.
-// If x cannot be represented in an uint64, the result is undefined.
+// If x cannot be represented in a uint64, the result is undefined.
 func (x *Int) Uint64() uint64 {
 	if len(x.abs) == 0 {
 		return 0

src/pkg/math/big/rat.go

--- a/src/pkg/math/big/rat.go
+++ b/src/pkg/math/big/rat.go
@@ -163,16 +163,16 @@ func quotToFloat(a, b nat) (f float64, exact bool) {
 	return
 }
 
-// Float64 returns the nearest float64 value to z.
-// If z is exactly representable as a float64, Float64 returns exact=true.
-// If z is negative, so too is f, even if f==0.
-func (z *Rat) Float64() (f float64, exact bool) {
-	b := z.b.abs
+// Float64 returns the nearest float64 value for x and a bool indicating
+// whether f represents x exactly. The sign of f always matches the sign
+// of x, even if f == 0.
+func (x *Rat) Float64() (f float64, exact bool) {
+	b := x.b.abs
 	if len(b) == 0 {
 		b = b.set(natOne) // materialize denominator
 	}
-	f, exact = quotToFloat(z.a.abs, b)
-	if z.a.neg {
+	f, exact = quotToFloat(x.a.abs, b)
+	if x.a.neg {
 		f = -f
 	}
 	return

src/pkg/math/big/rat_test.go

--- a/src/pkg/math/big/rat_test.go
+++ b/src/pkg/math/big/rat_test.go
@@ -503,9 +503,7 @@ func TestIssue3521(t *testing.T) {
 // Test inputs to Rat.SetString.  The prefix "long:\" causes the test
 // to be skipped in --test.short mode.  (The threshold is about 500us.)
 var float64inputs = []string{
-//
 // Constants plundered from strconv/testfp.txt.
-//
 
 	// Table 1: Stress Inputs for Conversion to 53-bit Binary, < 1/2 ULP
 	"5e+125",
@@ -583,9 +581,7 @@ var float64inputs = []string{
 	"75224575729e-45",
 	"459926601011e+15",
 
-//
 // Constants plundered from strconv/atof_test.go.
-//
 
 	"0",
 	"1",
@@ -734,7 +730,7 @@ func TestFloat64SpecialCases(t *testing.T) {
 			case f == 0 && r.Num().BitLen() == 0:
 				// Ok: Rat(0) is equivalent to both +/- float64(0).
 			default:
-				t.Errorf("strconv.ParseFloat(%q) = %g (%b), want %g (%b); delta=%g", input, e, e, f, f, f-e)
+				t.Errorf("strconv.ParseFloat(%q) = %g (%b), want %g (%b); delta = %g", input, e, e, f, f, f-e)
 			}
 		}
 
@@ -795,7 +791,7 @@ func TestFloat64Distribution(t *testing.T) {
 
 					if !checkIsBestApprox(t, f, r) {
 						// Append context information.
-						t.Errorf("(input was mantissa %#x, exp %d; f=%g (%b); f~%g; r=%v)",
+						t.Errorf("(input was mantissa %#x, exp %d; f = %g (%b); f ~ %g; r = %v)",
 						b, exp, f, f, math.Ldexp(float64(b), exp), r)
 					}
 
@@ -830,7 +826,7 @@ func checkNonLossyRoundtrip(t *testing.T, f float64) {
 	}
 	f2, exact := r.Float64()
 	if f != f2 || !exact {
-		t.Errorf("Rat.SetFloat64(%g).Float64() = %g (%b), %v, want %g (%b), %v; delta=%b",
+		t.Errorf("Rat.SetFloat64(%g).Float64() = %g (%b), %v, want %g (%b), %v; delta = %b",
 		f, f2, exact, f, f, true, f2-f)
 	}
 }

コアとなるコードの解説

src/pkg/math/big/int.go の変更点

  • SetUint64 の冗長なキャスト削除: z.abs = z.abs.setUint64(uint64(x)) から z.abs = z.abs.setUint64(x) への変更は、x が既に uint64 型であるため、uint64(x) という明示的なキャストが不要であることを示しています。Go言語では、引数の型が期待される型と完全に一致する場合、冗長なキャストは避けるべきです。これはコードの簡潔性を高め、読みやすくします。

  • Int64Uint64 への委譲: Int64() メソッドの内部実装が、直接 x.abs を操作する複雑なロジックから int64(x.Uint64()) へと変更されました。これは、コードの重複を避け、Uint64() が既に提供している堅牢な変換ロジックを再利用するためのリファクタリングです。 元のコードは、Int の内部表現 (x.absWord スライス) から直接 int64 を構築しようとしていました。特に、32ビットシステム (_W == 32) で int64 を構築するために2つの Word を結合するロジックが含まれていました。 新しい実装では、まず Uint64() を呼び出して符号なしの uint64 値を取得し、その結果を int64 にキャストします。その後、x.neg (負の符号) に応じて結果を反転させます。このアプローチは、Uint64() が既に Intuint64 に収まるかどうかのチェックと変換を行っているため、よりクリーンで保守しやすくなります。

  • Uint64 コメントの文法修正: "an uint64" から "a uint64" への変更は、英語の文法規則に従ったものです。uint64 の発音は母音で始まるため、通常は "an" を使用しますが、Goの文脈では "a" が使われることもあります。この修正は、コメントの品質を向上させるためのものです。

src/pkg/math/big/rat.go の変更点

  • (*Rat).Float64 のレシーバ名 z から x への変更: これは、Go言語の慣習に合わせた重要な変更です。Goでは、メソッドのレシーバ名について特定の推奨事項があります。

    • レシーバがポインタ型 (*Type) で、そのメソッドがレシーバの値を変更する場合、レシーバ名を型名の最初の1文字にすることが一般的です(例: (r *Rat))。
    • しかし、この Float64 メソッドのように、レシーバがポインタ型であっても、メソッドがレシーバの値を変更せず、別の型の値を返す場合(ここでは float64)、レシーバ名を x とすることが推奨されることがあります。これは、数学的な関数で入力変数を x とすることが一般的であることと、z が複素数を連想させるため、混乱を避ける目的があるかもしれません。 この変更により、Float64 メソッドの内部で z を参照していた箇所がすべて x に更新されています。
  • Float64 メソッドのコメント修正: 新しいコメントは、Float64 メソッドの動作をより正確に記述しています。特に、「The sign of f always matches the sign of x, even if f == 0.」という記述は重要です。IEEE 754 浮動小数点数標準では、正のゼロ (+0.0) と負のゼロ (-0.0) が存在します。math/big パッケージの Rat 型は符号を持つため、Rat がゼロであっても、その元の符号(分子の符号)が float64 のゼロに引き継がれることを明確にしています。これにより、Rat から float64 への変換における挙動がより予測可能になります。

src/pkg/math/big/rat_test.go の変更点

  • テストコメントの削除: float64inputs 変数の定義周辺にあったコメント行が削除されました。これらはテストデータの出所を示すものでしたが、コードの簡潔性を優先して削除されたと考えられます。機能的な影響はありません。

  • テストエラーメッセージのフォーマット調整: t.Errorf 関数内で生成されるエラーメッセージのフォーマットが変更されました。具体的には、delta=%g のような部分が delta = %g のように、等号やチルダの周りにスペースが追加されました。これは、テストが失敗した際に表示されるエラーメッセージの視覚的な可読性を向上させるためのものです。スペースを追加することで、各要素がより明確に区別され、デバッグ時に問題の原因を特定しやすくなります。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント
  • Go言語のソースコード
  • Stack Overflow や Goコミュニティの議論 (一般的なGoの慣習について)