[インデックス 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) を扱うための重要なパッケージであり、その正確性と信頼性は非常に重要です。
このコミットで行われた変更は、主に以下の点に焦点を当てています。
- コメントの正確性向上: コードの意図をより正確に反映させるためのコメント修正。これは、コードの理解を深め、将来の開発者が誤解するリスクを減らすために不可欠です。
- 変数名の慣習への準拠: Go言語のコードベースでは、特定のパターンに従った変数名の慣習が存在します。特に、レシーバ変数(メソッドの最初の引数)の名前付けには慣習があり、このコミットでは
(*Rat).Float64メソッドにおいて、その慣習に合わせるための変更が行われました。具体的には、レシーバがポインタ型でなく、かつその型が値を返す関数である場合、レシーバ名をxとすることが推奨される場合があります。この変更は、コードベース全体の一貫性を保ち、Goコミュニティの慣習に従うことを目的としています。 - テスト出力の調整: テストの出力がより明確になるように微調整が行われました。これは、テスト結果の解釈を容易にし、デバッグプロセスを効率化するために重要です。
これらの変更は、機能的なバグ修正というよりも、コードの「衛生状態」を改善し、長期的な保守性を高めるためのものです。
前提知識の解説
このコミットを理解するためには、以下の前提知識が役立ちます。
1. Go言語の math/big パッケージ
math/big パッケージは、Go言語で任意精度の数値を扱うための機能を提供します。これは、標準の int や float64 型では表現できない非常に大きな数値や、高い精度が要求される計算を行う場合に必要となります。
Int: 任意精度の整数型。Rat: 任意精度の有理数型(分数)。分子と分母をInt型で持ちます。Float: 任意精度の浮動小数点数型。
これらの型は、金融計算、暗号化、科学技術計算など、精度が重要なアプリケーションで広く使用されます。
2. Go言語のレシーバ変数名慣習
Go言語では、メソッドのレシーバ変数(func (r ReceiverType) MethodName(...) の r の部分)には特定の命名慣習があります。
- レシーバがポインタ型 (
*Type) の場合、通常は型名の最初の1文字(例:*Intならi、*Ratならr)を使用します。 - レシーバが値型 (
Type) の場合も同様に型名の最初の1文字を使用することが多いですが、特にそのメソッドがレシーバの値を変更せず、単に値を返すような場合、xやvのような一般的な変数名が使われることがあります。このコミットの(*Rat).Float64のケースでは、Float64がRatの値を変更せず、float64の値を返すため、レシーバ名をzからxに変更することで、この慣習に合わせようとしています。
3. 任意精度演算の内部表現
math/big パッケージの数値は、内部的には nat (natural number) 型のスライス([]Word)で表現されます。Word はプラットフォームのワードサイズ(32ビットまたは64ビット)に応じた符号なし整数型です。これにより、メモリが許す限り任意の大きさの数値を表現できます。
Int型は、abs(絶対値) とneg(負の符号) のフィールドを持ちます。absはnat型で、数値の絶対値を表します。Rat型は、a(分子) とb(分母) のInt型フィールドを持ちます。
4. 浮動小数点数 (float64) の表現
Go言語の float64 は、IEEE 754 倍精度浮動小数点数標準に準拠しています。これは、符号ビット、指数部、仮数部で構成され、非常に広い範囲の数値を表現できますが、精度には限界があります。任意精度有理数から float64 への変換は、この精度の限界内で最も近い表現を見つけるプロセスです。
技術的詳細
このコミットは、以下の3つのファイルにわたる変更を含んでいます。
src/pkg/math/big/int.gosrc/pkg/math/big/rat.gosrc/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()メソッドは、Intがuint64で表現可能かどうかを適切に処理し、その値を返します。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.absf, 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. - 新しいコメントは、返される
fがxを正確に表現しているかどうかを示す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 = %gf=%g (%b); f~%g; r=%v->f = %g (%b); f ~ %g; r = %vdelta=%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言語では、引数の型が期待される型と完全に一致する場合、冗長なキャストは避けるべきです。これはコードの簡潔性を高め、読みやすくします。 -
Int64のUint64への委譲:Int64()メソッドの内部実装が、直接x.absを操作する複雑なロジックからint64(x.Uint64())へと変更されました。これは、コードの重複を避け、Uint64()が既に提供している堅牢な変換ロジックを再利用するためのリファクタリングです。 元のコードは、Intの内部表現 (x.absのWordスライス) から直接int64を構築しようとしていました。特に、32ビットシステム (_W == 32) でint64を構築するために2つのWordを結合するロジックが含まれていました。 新しい実装では、まずUint64()を呼び出して符号なしのuint64値を取得し、その結果をint64にキャストします。その後、x.neg(負の符号) に応じて結果を反転させます。このアプローチは、Uint64()が既にIntがuint64に収まるかどうかのチェックと変換を行っているため、よりクリーンで保守しやすくなります。 -
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言語
math/bigパッケージのドキュメント: https://pkg.go.dev/math/big - Go言語のコードレビューコメント慣習 (Go Code Review Comments): https://go.dev/doc/effective_go#commentary (特に命名規則について)
- IEEE 754 浮動小数点数標準: https://en.wikipedia.org/wiki/IEEE_754
参考にした情報源リンク
- Go言語の公式ドキュメント
- Go言語のソースコード
- Stack Overflow や Goコミュニティの議論 (一般的なGoの慣習について)