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

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

このコミットは、Go言語の標準ライブラリであるfmtパッケージにおいて、complex64およびcomplex128型の値に対する%bフォーマット動詞のサポートを追加するものです。これにより、複素数型の実部と虚部をIEEE 754のバイナリ表現(指数部と仮数部)で出力できるようになります。

コミット

commit 89dacb9cca0a27da1efe0578cd0881b80b13ed45
Author: Rob Pike <r@golang.org>
Date:   Sun Sep 15 10:45:36 2013 +1000

    fmt: %b for complex64 and complex128
    Just an oversight they were missing.
    Fixes #6387
    
    R=golang-dev, dominik.honnef, rsc
    CC=golang-dev
    https://golang.org/cl/13715043

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

https://github.com/golang/go/commit/89dacb9cca0a27da1efe0578cd0881b80b13ed45

元コミット内容

fmt: %b for complex64 and complex128 Just an oversight they were missing. Fixes #6387

このコミットは、fmtパッケージにおいて、complex64型とcomplex128型の値に対して%bフォーマット動詞が欠落していたのを修正するものです。これは単なる見落としであったと説明されています。関連するIssueは#6387です。

変更の背景

Go言語のfmtパッケージは、様々なデータ型を整形して出力するための機能を提供します。Printf関数などで使用されるフォーマット動詞は、数値、文字列、ブール値など、多様な型に対応しています。浮動小数点数型(float32, float64)に対しては、以前から%bフォーマット動詞がサポートされており、これは数値をIEEE 754標準に基づくバイナリ表現(指数部と仮数部)で出力する機能を提供していました。

しかし、複素数型(complex64, complex128)については、その実部と虚部がそれぞれ浮動小数点数であるにもかかわらず、%bフォーマット動詞がサポートされていませんでした。これは、一貫性の観点からも、またデバッグや低レベルな数値表現の確認が必要な場面においても不便でした。このコミットは、この機能の欠落が単なる「見落とし (oversight)」であったと認識され、その修正が行われたものです。Issue #6387がこの問題の報告と修正のきっかけとなりました。

前提知識の解説

Go言語のfmtパッケージ

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

  • Printf関数: 指定されたフォーマット文字列に従って値を整形し、標準出力に出力します。
  • フォーマット動詞: フォーマット文字列内で使用される特殊な記号で、値の型や表示形式を指定します。例えば、%dは整数、%sは文字列、%fは浮動小数点数を表します。

%bフォーマット動詞

%bフォーマット動詞は、浮動小数点数を「指数表記のバイナリ表現」で出力するために使用されます。具体的には、IEEE 754浮動小数点数標準における仮数部と指数部を基にした形式で表示されます。

例:

  • 1.0 (float64) は 4503599627370496p-52 と表示されます。
    • 4503599627370496 は仮数部(mantissa)を整数として表現したものです。
    • p-52 は2の指数部(exponent)を表し、2^-52を意味します。
    • これは 4503599627370496 * 2^-521.0 に等しいことを示しています。

IEEE 754浮動小数点数標準

IEEE 754は、浮動小数点数のコンピュータ上での表現方法に関する国際標準です。この標準は、単精度(32ビット、float32に相当)と倍精度(64ビット、float64に相当)の形式を定義しており、それぞれ符号ビット、指数部、仮数部から構成されます。

  • 符号ビット (Sign bit): 数の正負を表します(0が正、1が負)。
  • 指数部 (Exponent): 2のべき乗の指数を表します。バイアス形式で格納されます。
  • 仮数部 (Mantissa/Significand): 有効数字を表します。通常、正規化された形式では先頭の1ビットは省略されます(隠しビット)。

%bフォーマットは、このIEEE 754の内部表現を人間が読める形式で出力する際に非常に有用です。

Go言語の複素数型

Go言語には、複素数を扱うための組み込み型があります。

  • complex64: float32の実部と虚部を持つ複素数型です。
  • complex128: float64の実部と虚部を持つ複素数型です。

複素数は real + imag * i の形式で表され、realimagはそれぞれ浮動小数点数です。

技術的詳細

このコミットの技術的な核心は、fmtパッケージが複素数型をどのように処理し、%bフォーマット動詞をどのように適用するかという点にあります。

fmtパッケージの内部では、pp (printer) 構造体がフォーマット処理を担当し、fmt構造体が具体的なフォーマットロジックをカプセル化しています。複素数型をフォーマットする際には、その実部と虚部を個別に浮動小数点数として抽出し、それぞれの部分に対して適切なフォーマット処理を適用する必要があります。

変更前は、complex64complex128のフォーマット処理において、%e, %E, %f, %F, %g, %Gといった一般的な浮動小数点数フォーマット動詞のみがサポートされていました。%bはリストに含まれていませんでした。

このコミットでは、以下の変更が行われました。

  1. fmt_c64およびfmt_c128メソッドの更新: これらのメソッドは、それぞれcomplex64complex128のフォーマット処理を担当します。内部で実部と虚部を抽出し、それぞれの部分に対して指定されたフォーマット動詞を適用します。今回の変更では、switch verb文に'b'ケースが追加され、実部と虚部をfmt_fb32float32用)またはfmt_fb64float64用)でフォーマットするように指示しています。
  2. fmtComplex64およびfmtComplex128関数の更新: これらの関数は、pp構造体から呼び出され、特定のフォーマット動詞が複素数型に適用可能かどうかを判断します。変更前は%bが許可されていませんでしたが、今回の変更で'b'が許可されるフォーマット動詞のリストに追加されました。

これにより、fmt.Printf("%b", complex(1, 2))のような呼び出しがあった場合、fmtパッケージは複素数の実部(1)と虚部(2)をそれぞれ浮動小数点数として抽出し、それらを個別に%bフォーマットで処理し、最終的に(実部の%b表現 + 虚部の%b表現i)のような形式で出力するようになります。

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

このコミットでは、主に以下の3つのファイルが変更されています。

  1. src/pkg/fmt/fmt_test.go: 新しい機能のテストケースが追加されています。
  2. src/pkg/fmt/format.go: 複素数型のフォーマットロジックが変更されています。
  3. src/pkg/fmt/print.go: 複素数型に対する%bフォーマット動詞の許可が追加されています。

src/pkg/fmt/fmt_test.go

--- a/src/pkg/fmt/fmt_test.go
+++ b/src/pkg/fmt/fmt_test.go
@@ -227,6 +227,8 @@ var fmtTests = []struct {
 	{"%+.3g", -1.0, "-1"},
 	{"% .3g", -1.0, "-1"},
 	{"% .3g", 1.0, " 1"},
+	{"%b", float32(1.0), "8388608p-23"},
+	{"%b", 1.0, "4503599627370496p-52"},
 
 	// complex values
 	{"%+.3e", 0i, "(+0.000e+00+0.000e+00i)"},
@@ -247,6 +249,8 @@ var fmtTests = []struct {
 	{"% .3E", -1 - 2i, "(-1.000E+00-2.000E+00i)"},
 	{"%+.3g", complex64(1 + 2i), "(+1+2i)"},
 	{"%+.3g", complex128(1 + 2i), "(+1+2i)"},
+	{"%b", complex64(1 + 2i), "(8388608p-23+8388608p-22i)"},
+	{"%b", 1 + 2i, "(4503599627370496p-52+4503599627370496p-51i)"},
 
 	// erroneous formats
 	{"", 2, "%!(EXTRA int=2)"},

src/pkg/fmt/format.go

--- a/src/pkg/fmt/format.go
+++ b/src/pkg/fmt/format.go
@@ -429,6 +429,8 @@ func (f *fmt) fmt_c64(v complex64, verb rune) {
 	oldPlus := f.plus
 	for i := 0; ; i++ {
 		switch verb {
+		case 'b':
+			f.fmt_fb32(r)
 		case 'e':
 			f.fmt_e32(r)
 		case 'E':
@@ -457,6 +459,8 @@ func (f *fmt) fmt_c128(v complex128, verb rune) {
 	oldPlus := f.plus
 	for i := 0; ; i++ {
 		switch verb {
+		case 'b':
+			f.fmt_fb64(r)
 		case 'e':
 			f.fmt_e64(r)
 		case 'E':

src/pkg/fmt/print.go

--- a/src/pkg/fmt/print.go
+++ b/src/pkg/fmt/print.go
@@ -511,7 +511,7 @@ func (p *pp) fmtFloat64(v float64, verb rune) {
 
 func (p *pp) fmtComplex64(v complex64, verb rune) {
 	switch verb {
-	case 'e', 'E', 'f', 'F', 'g', 'G':
+	case 'b', 'e', 'E', 'f', 'F', 'g', 'G':
 		p.fmt.fmt_c64(v, verb)
 	case 'v':
 		p.fmt.fmt_c64(v, 'g')
@@ -522,7 +522,7 @@ func (p *pp) fmtComplex64(v complex64, verb rune) {
 
 func (p *pp) fmtComplex128(v complex128, verb rune) {
 	switch verb {
-	case 'e', 'E', 'f', 'F', 'g', 'G':
+	case 'b', 'e', 'E', 'f', 'F', 'g', 'G':
 		p.fmt.fmt_c128(v, verb)
 	case 'v':
 		p.fmt.fmt_c128(v, 'g')

コアとなるコードの解説

src/pkg/fmt/fmt_test.go

このファイルでは、fmtTestsというテストケースのスライスに新しいエントリが追加されています。

  • {"%b", float32(1.0), "8388608p-23"}: float321.0%bで正しくフォーマットされることを確認します。
  • {"%b", 1.0, "4503599627370496p-52"}: float641.0%bで正しくフォーマットされることを確認します。
  • {"%b", complex64(1 + 2i), "(8388608p-23+8388608p-22i)"}: complex641+2i%bで正しくフォーマットされることを確認します。実部と虚部がそれぞれfloat32として%bフォーマットされます。
  • {"%b", 1 + 2i, "(4503599627370496p-52+4503599627370496p-51i)"}: complex1281+2i%bで正しくフォーマットされることを確認します。実部と虚部がそれぞれfloat64として%bフォーマットされます。

これらのテストケースは、変更が期待通りに動作し、既存の%bフォーマットの動作も維持されていることを保証します。

src/pkg/fmt/format.go

このファイルには、fmt構造体のfmt_c64fmt_c128メソッドが含まれています。これらは、それぞれcomplex64complex128の具体的なフォーマットロジックを処理します。

  • func (f *fmt) fmt_c64(v complex64, verb rune):
    • switch verb文にcase 'b':が追加されました。
    • このケースでは、f.fmt_fb32(r)が呼び出されます。rcomplex64の実部または虚部(float32型)です。fmt_fb32float32%b形式でフォーマットするための内部関数です。
  • func (f *fmt) fmt_c128(v complex128, verb rune):
    • 同様に、switch verb文にcase 'b':が追加されました。
    • このケースでは、f.fmt_fb64(r)が呼び出されます。rcomplex128の実部または虚部(float64型)です。fmt_fb64float64%b形式でフォーマットするための内部関数です。

これらの変更により、fmtパッケージは複素数の実部と虚部を個別に抽出し、それぞれを対応する浮動小数点数型の%bフォーマットロジックに渡すことができるようになりました。

src/pkg/fmt/print.go

このファイルには、pp構造体のfmtComplex64fmtComplex128関数が含まれています。これらは、Printfのような高レベルの関数から呼び出され、与えられた複素数型とフォーマット動詞に基づいて、適切なフォーマット処理をディスパッチします。

  • func (p *pp) fmtComplex64(v complex64, verb rune):
    • switch verb文のcaseリストに'b'が追加されました。
    • これにより、complex64型に対して%bフォーマット動詞が指定された場合、p.fmt.fmt_c64(v, verb)が呼び出されるようになります。
  • func (p *pp) fmtComplex128(v complex128, verb rune):
    • 同様に、switch verb文のcaseリストに'b'が追加されました。
    • これにより、complex128型に対して%bフォーマット動詞が指定された場合、p.fmt.fmt_c128(v, verb)が呼び出されるようになります。

これらの変更は、%bフォーマット動詞が複素数型に対して「有効な」動詞として認識されるようにするためのものです。これにより、fmtパッケージは複素数型と%b動詞の組み合わせを正しく処理できるようになります。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント
  • GitHubのgolang/goリポジトリのコミット履歴とIssueトラッカー
  • IEEE 754に関する一般的な情報源(Wikipediaなど)