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

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

このコミットは、Go言語の標準ライブラリであるfmtパッケージにおいて、浮動小数点数および複素数の書式設定(フォーマット)において、フォーマット動詞%F%fの同義語として扱われるように変更するものです。これにより、%fと同様に、指数表記ではなく小数点表記で数値を整形できるようになります。

コミット

  • コミットハッシュ: b00d967706377725b11acc16478e645ca7dd4431
  • 作者: Rob Pike r@golang.org
  • コミット日時: 2014年3月20日 木曜日 08:51:06 +1100

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

https://github.com/golang/go/commit/b00d967706377725b11acc16478e645ca7dd4431

元コミット内容

fmt: make %F a synonym for %f
Rationale:
        It already is for scanning.
        It is accepted for complexes already, but doesn't work.
        It's analogous to %G and %E.
        C accepts it too, and we try to be roughly compatible.
Fixes #7518.

LGTM=bradfitz
R=golang-codereviews, bradfitz
CC=golang-codereviews
https://golang.org/cl/77580044

変更の背景

この変更は、Go言語のfmtパッケージにおける浮動小数点数および複素数のフォーマット動詞の整合性を向上させるために行われました。コミットメッセージに記載されている主な理由は以下の通りです。

  1. スキャン時との整合性: fmtパッケージのScan関数群(例: fmt.Sscanf)では、既に%F%fの同義語として認識されていました。しかし、フォーマット(出力)時にはこの対応がされていなかったため、一貫性がありませんでした。
  2. 複素数型での挙動の修正: 複素数型(complex64, complex128)のフォーマットにおいて、%Fは受け入れられていたものの、実際には正しく機能していませんでした。このコミットにより、複素数に対しても%Fが期待通りに動作するようになります。
  3. %G%Eとの類似性: fmtパッケージには、%g%G%e%Eのように、大文字と小文字で異なるフォーマット動詞が存在し、それぞれが特定の書式(例: %eは小文字のeを使った指数表記、%Eは大文字のEを使った指数表記)に対応しています。これに倣い、%f(小数点表記)に対して%Fを同義語とすることで、より直感的な命名規則に近づけます。
  4. C言語との互換性: C言語のprintf関数では、%F%fの同義語として定義されています。Go言語のfmtパッケージはC言語のprintfに影響を受けている部分が多く、可能な限り互換性を保つことで、C言語の経験がある開発者にとって学習コストを低減し、期待通りの挙動を提供することを目指しています。

これらの理由から、GitHub Issue #7518で報告された問題に対応し、fmtパッケージのフォーマット動詞の挙動をより予測可能で一貫性のあるものにすることが目的です。

前提知識の解説

Go言語の fmt パッケージ

fmtパッケージは、Go言語におけるI/Oフォーマットを実装するためのパッケージです。C言語のprintfscanfに似た機能を提供し、様々なデータ型を文字列に変換したり(フォーマット)、文字列からデータ型を解析したり(スキャン)することができます。

主な関数には以下のようなものがあります。

  • fmt.Printf: 標準出力にフォーマットされた文字列を出力します。
  • fmt.Sprintf: フォーマットされた文字列を返します。
  • fmt.Fprint: 指定されたio.Writerにフォーマットされた文字列を出力します。
  • fmt.Scanf: 標準入力からフォーマットされたデータを読み取ります。
  • fmt.Sscanf: 文字列からフォーマットされたデータを読み取ります。

フォーマット動詞(Verbs)

fmtパッケージでは、フォーマット文字列内で%に続く文字(フォーマット動詞)を使って、値の表示形式を指定します。

  • 浮動小数点数(float32, float64)のフォーマット動詞:
    • %e: 指数表記(例: -1.234e+05)。小文字のeを使用。
    • %E: 指数表記(例: -1.234E+05)。大文字のEを使用。
    • %f: 小数点表記(例: 123.456)。指数表記は使用しない。
    • %g: %eまたは%fのうち、よりコンパクトな出力になる方を選択します。
    • %G: %Eまたは%fのうち、よりコンパクトな出力になる方を選択します。

複素数(complex64, complex128)のフォーマット

Go言語の複素数型は、実部と虚部から構成されます。fmtパッケージで複素数をフォーマットする場合、通常は実部と虚部がそれぞれ浮動小数点数としてフォーマットされます。例えば、%fを使って複素数をフォーマットすると、(実部.xxx+虚部.yyyi)のような形式になります。

C言語の printf との関連

C言語の標準ライブラリにはprintf関数があり、Goのfmtパッケージと同様にフォーマット文字列と引数を使って出力を行います。C言語のprintfでは、浮動小数点数のフォーマットに%f, %e, %gなどが使われます。また、C99標準以降では、%F%fの同義語として導入されており、Goのfmtパッケージもこれに倣うことで、より広い互換性を提供しています。

技術的詳細

このコミットの技術的な詳細は、fmtパッケージの内部実装において、浮動小数点数および複素数のフォーマット処理を行う関数が、フォーマット動詞%fだけでなく%Fも受け入れるように変更された点にあります。

具体的には、以下のファイルが変更されています。

  1. src/pkg/fmt/doc.go:

    • fmtパッケージのドキュメントに、%F%fの同義語であることが明記されます。これは、ユーザーがfmtパッケージのドキュメントを参照した際に、%Fの挙動を理解できるようにするための変更です。
  2. src/pkg/fmt/fmt_test.go:

    • %F%fと同様に機能することを検証するための新しいテストケースが追加されます。これにより、変更が正しく実装され、将来のリグレッションを防ぐことができます。テストケースは、float32float64complex64complex128の各型に対して%Fが期待通りの出力を生成するかを確認します。
  3. src/pkg/fmt/format.go:

    • このファイルには、fmtパッケージが内部的に使用するフォーマットロジックが含まれています。特に、fmt構造体のfmt_c64complex64用)およびfmt_c128complex128用)メソッドが変更されます。これらのメソッド内で、フォーマット動詞を処理するswitch文に'F'が追加され、'f'と同じ処理(小数点表記でのフォーマット)が実行されるようになります。
  4. src/pkg/fmt/print.go:

    • このファイルには、fmtパッケージのPrint系関数(Printfなど)が実際に値を文字列に変換する際の低レベルなロジックが含まれています。特に、pp構造体のfmtFloat32float32用)およびfmtFloat64float64用)メソッドが変更されます。これらのメソッド内で、フォーマット動詞を処理するswitch文に'F'が追加され、'f'と同じ処理(小数点表記でのフォーマット)が実行されるようになります。

これらの変更により、fmtパッケージは%F%fと全く同じように扱い、浮動小数点数や複素数を小数点表記でフォーマットする際に、大文字のFを使用しても期待通りの結果が得られるようになります。

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

diff --git a/src/pkg/fmt/doc.go b/src/pkg/fmt/doc.go
index 7a7b63bd6b..7a14b80894 100644
--- a/src/pkg/fmt/doc.go
+++ b/src/pkg/fmt/doc.go
@@ -37,6 +37,7 @@
 		%e	scientific notation, e.g. -1234.456e+78
 		%E	scientific notation, e.g. -1234.456E+78
 		%f	decimal point but no exponent, e.g. 123.456
+		%F	synonym for %f
 		%g	whichever of %e or %f produces more compact output
 		%G	whichever of %E or %f produces more compact output
 	String and slice of bytes:
diff --git a/src/pkg/fmt/fmt_test.go b/src/pkg/fmt/fmt_test.go
index 14a1a56c04..c7a09dedd9 100644
--- a/src/pkg/fmt/fmt_test.go
+++ b/src/pkg/fmt/fmt_test.go
@@ -220,6 +220,8 @@ var fmtTests = []struct {
 	{"%+.3e", 0.0, "+0.000e+00"},
 	{"%+.3e", 1.0, "+1.000e+00"},
 	{"%+.3f", -1.0, "-1.000"},
+	{"%+.3F", -1.0, "-1.000"},
+	{"%+.3F", float32(-1.0), "-1.000"},
 	{"%+07.2f", 1.0, "+001.00"},
 	{"%+07.2f", -1.0, "-001.00"},
 	{"% .3E", -1.0, "-1.000E+00"},
@@ -241,6 +243,8 @@ var fmtTests = []struct {
 	{"%+.3g", 1 + 2i, "(+1+2i)"},
 	{"%.3e", 0i, "(0.000e+00+0.000e+00i)"},
 	{"%.3f", 0i, "(0.000+0.000i)"},
+	{"%.3F", 0i, "(0.000+0.000i)"},
+	{"%.3F", complex64(0i), "(0.000+0.000i)"},
 	{"%.3g", 0i, "(0+0i)"},
 	{"%.3e", 1 + 2i, "(1.000e+00+2.000e+00i)"},
 	{"%.3f", 1 + 2i, "(1.000+2.000i)"},
diff --git a/src/pkg/fmt/format.go b/src/pkg/fmt/format.go
index 3835aa9823..b0f4ad4b73 100644
--- a/src/pkg/fmt/format.go
+++ b/src/pkg/fmt/format.go
@@ -447,7 +447,7 @@ func (f *fmt) fmt_c64(v complex64, verb rune) {
 		case 'E':
 			f.fmt_E32(r)
 		case 'f':
-			f.fmt_f32(r)
+		case 'f', 'F':
+			f.fmt_f32(r)
 		case 'g':
 			f.fmt_g32(r)
@@ -477,7 +477,7 @@ func (f *fmt) fmt_c128(v complex128, verb rune) {
 		case 'E':
 			f.fmt_E64(r)
 		case 'f':
-			f.fmt_f64(r)
+		case 'f', 'F':
+			f.fmt_f64(r)
 		case 'g':
 			f.fmt_g64(r)
diff --git a/src/pkg/fmt/print.go b/src/pkg/fmt/print.go
index 2f13bcd95e..c56d5b9401 100644
--- a/src/pkg/fmt/print.go
+++ b/src/pkg/fmt/print.go
@@ -447,7 +447,7 @@ func (p *pp) fmtFloat32(v float32, verb rune) {
 		p.fmt.fmt_e32(v)
 	case 'E':
 		p.fmt.fmt_E32(v)
-	case 'f':
+	case 'f', 'F':
 		p.fmt.fmt_f32(v)
 	case 'g', 'v':
 		p.fmt.fmt_g32(v)
@@ -466,7 +466,7 @@ func (p *pp) fmtFloat64(v float64, verb rune) {
 		p.fmt.fmt_e64(v)
 	case 'E':
 		p.fmt.fmt_E64(v)
-	case 'f':
+	case 'f', 'F':
 		p.fmt.fmt_f64(v)
 	case 'g', 'v':
 		p.fmt.fmt_g64(v)

コアとなるコードの解説

src/pkg/fmt/doc.go の変更

  • 追加行: + %F synonym for %f
  • 解説: fmtパッケージの公式ドキュメントに、%F%fの同義語であることを明示的に追加しています。これにより、開発者がfmtパッケージのフォーマット動詞について調べた際に、%Fの挙動を正確に理解できるようになります。これは機能的な変更ではなく、ドキュメンテーションの改善です。

src/pkg/fmt/fmt_test.go の変更

  • 追加行:
    • + {"%+.3F", -1.0, "-1.000"},
    • + {"%+.3F", float32(-1.0), "-1.000"},
    • + {"%.3F", 0i, "(0.000+0.000i)"},
    • + {"%.3F", complex64(0i), "(0.000+0.000i)"},
  • 解説: fmtパッケージのテストスイートに、%Fフォーマット動詞の新しいテストケースが追加されています。
    • 最初の2つのテストケースは、float64float32の負の値に対して%+.3F(符号付き、小数点以下3桁、%Fフォーマット)が正しく-1.000とフォーマットされることを確認しています。
    • 次の2つのテストケースは、complex128complex64のゼロ値に対して%.3F(小数点以下3桁、%Fフォーマット)が正しく(0.000+0.000i)とフォーマットされることを確認しています。
    • これらのテストは、%F%fと同じフォーマット結果を生成することを保証し、この変更が意図通りに機能していることを検証します。

src/pkg/fmt/format.go の変更

  • 変更箇所:
    • case 'f':case 'f', 'F': に変更。
  • 解説: このファイルは、複素数型(complex64complex128)のフォーマット処理を担当するfmt_c64およびfmt_c128メソッドを含んでいます。変更前は、'f'というフォーマット動詞のみがfmt_f32float32の実部/虚部をフォーマット)またはfmt_f64float64の実部/虚部をフォーマット)を呼び出していましたが、この変更により'F'も同じ処理を呼び出すようになりました。これにより、複素数を%Fでフォーマットしようとした際に、正しく小数点表記で実部と虚部が整形されるようになります。

src/pkg/fmt/print.go の変更

  • 変更箇所:
    • case 'f':case 'f', 'F': に変更。
  • 解説: このファイルは、浮動小数点数型(float32float64)のフォーマット処理を担当するfmtFloat32およびfmtFloat64メソッドを含んでいます。変更前は、'f'というフォーマット動詞のみがfmt_f32またはfmt_f64を呼び出していましたが、この変更により'F'も同じ処理を呼び出すようになりました。これにより、浮動小数点数を%Fでフォーマットしようとした際に、正しく小数点表記で整形されるようになります。

これらのコード変更は、fmtパッケージの内部でフォーマット動詞を処理するswitch文に'F'を追加することで、%F%fと全く同じフォーマットロジックを共有するようにしています。これにより、コードの重複を避けつつ、%Fの新しい挙動を効率的に実装しています。

関連リンク

参考にした情報源リンク