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

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

このコミットは、Go言語の標準ライブラリである strconv パッケージ内の extfloat.go ファイルに対する変更です。具体的には、浮動小数点数のビット表現を扱う floatBits 関数から、不要な goto ステートメントを削除し、コードの可読性を向上させています。

コミット

commit 45b4867d0edabb5f2f8ee07d5f5e9b53f9f4dbe0
Author: Robin Eklind <r.eklind.87@gmail.com>
Date:   Wed Nov 14 09:42:48 2012 -0800

    strconv: Removed unnecessary use of goto. Made code easier to read.
    
    R=gri
    CC=gobot, golang-dev
    https://golang.org/cl/6855048

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

https://github.com/golang/go/commit/45b4867d0edabb5f2f8ee07d5f5e9b53f9f4dbe0

元コミット内容

strconv: Removed unnecessary use of goto. Made code easier to read.

このコミットは、strconv パッケージにおいて、不要な goto ステートメントを削除し、コードの可読性を向上させることを目的としています。

変更の背景

Go言語では、goto ステートメントの使用は推奨されていません。goto はプログラムの制御フローを複雑にし、コードの理解やデバッグを困難にする「スパゲッティコード」を生み出す原因となることがあります。このコミットは、既存のコードベースからこのような goto の使用を排除し、よりクリーンで保守しやすいコードに改善するという、一般的なソフトウェア開発のベストプラクティスに沿ったものです。特に、浮動小数点数の特殊なケース(無限大や非正規化数)を処理するロジックにおいて goto が使用されており、これをより構造化された if-else if 構造に置き換えることで、コードの意図が明確になり、エラーの発生しにくいコードへと改善されています。

前提知識の解説

Go言語における goto ステートメント

goto ステートメントは、プログラムの実行を指定されたラベルに無条件にジャンプさせる制御構造です。Go言語では goto の使用は可能ですが、その使用は非常に限定的であり、ほとんどのケースで for ループ、if/elseswitch などのより構造化された制御フローで代替可能です。goto は、ネストされたループからの脱出など、特定の状況でのみ考慮されるべきであり、一般的にはコードの可読性と保守性を低下させるため、避けるべきとされています。

IEEE 754 浮動小数点数標準

このコミットが関連する extfloat.go ファイルは、浮動小数点数の内部表現を扱っています。浮動小数点数は、IEEE 754 標準に基づいて表現されます。この標準は、コンピュータが浮動小数点数をどのように格納し、演算するかを定義しています。

  • 正規化数 (Normalized Numbers): ほとんどの浮動小数点数は正規化数として表現されます。これは、仮数部が常に1で始まることを意味し、精度を最大化します。
  • 非正規化数 (Denormalized Numbers): 非常に小さい数を表現するために使用されます。これらの数は、仮数部が1で始まらないため、正規化数よりも精度が低い場合があります。アンダーフロー(非常に小さい数が0になること)を防ぐために重要です。
  • 無限大 (Infinities): 正の無限大 (+Inf) と負の無限大 (-Inf) があります。これは、オーバーフロー(非常に大きい数が表現範囲を超えること)や、0による除算などの結果として生じます。
  • NaN (Not a Number): 不定形な結果(例: 0/0、無限大/無限大)を表します。

floatBits 関数は、これらの特殊なケースを含む浮動小数点数を、そのビット表現に変換する役割を担っています。

strconv パッケージ

Go言語の strconv パッケージは、基本的なデータ型(文字列、整数、浮動小数点数、真偽値など)間の変換機能を提供します。例えば、文字列を整数に変換したり、浮動小数点数を文字列に変換したりする際に使用されます。このパッケージは、数値のパースやフォーマットにおいて非常に重要であり、その内部では数値のビット表現を正確に扱う必要があります。extfloat.go は、このパッケージの内部で、特に浮動小数点数の詳細なビット操作を行うために使用されるユーティリティファイルです。

技術的詳細

このコミットの技術的な詳細としては、floatBits 関数内の制御フローの変更が挙げられます。元のコードでは、無限大のケースと非正規化数のケースを処理するために goto overflowgoto out という2つの goto ステートメントが使用されていました。

  • if exp-flt.bias >= 1<<flt.expbits-1 { goto overflow }
    • これは、指数部が最大値に達した場合(無限大を表す場合)に overflow ラベルにジャンプしていました。
  • if mant&(1<<flt.mantbits) == 0 { exp = flt.bias } goto out
    • これは、仮数部が非正規化数であることを示す場合に指数部を調整し、その後 out ラベルにジャンプしていました。

新しいコードでは、これらの goto ステートメントが if-else if 構造に置き換えられています。

	if exp-flt.bias >= 1<<flt.expbits-1 {
		// ±Inf
		mant = 0
		exp = 1<<flt.expbits - 1 + flt.bias
		overflow = true
	} else if mant&(1<<flt.mantbits) == 0 {
		// Denormalized?
		exp = flt.bias
	}

この変更により、コードの実行パスが線形になり、特定の条件が満たされた場合にのみ特定の処理ブロックが実行されるという、より予測可能で理解しやすい流れになります。

  • まず、無限大の条件 (exp-flt.bias >= 1<<flt.expbits-1) をチェックします。この条件が真であれば、無限大の処理(mant を0に、exp を無限大の表現に設定し、overflowtrue にする)が実行されます。
  • もし最初の if 条件が偽であれば、else if ブロックに進み、非正規化数の条件 (mant&(1<<flt.mantbits) == 0) をチェックします。この条件が真であれば、非正規化数の処理(expflt.bias に設定する)が実行されます。

この構造は、各条件が独立して評価され、適切な処理が適用されることを保証します。goto を使用していた場合、コードを追う際にジャンプ先のラベルを探す必要がありましたが、if-else if ではブロックの範囲内で処理が完結するため、コードの局所性が高まり、理解が容易になります。

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

変更は src/pkg/strconv/extfloat.go ファイルの floatBits 関数内で行われています。

--- a/src/pkg/strconv/extfloat.go
+++ b/src/pkg/strconv/extfloat.go
@@ -152,22 +152,14 @@ func (f *extFloat) floatBits(flt *floatInfo) (bits uint64, overflow bool) {

 	// Infinities.
 	if exp-flt.bias >= 1<<flt.expbits-1 {
-		goto overflow
-	}

-	// Denormalized?
-	if mant&(1<<flt.mantbits) == 0 {
+		// ±Inf
+		mant = 0
+		exp = 1<<flt.expbits - 1 + flt.bias
+		overflow = true
+	} else if mant&(1<<flt.mantbits) == 0 {
+		// Denormalized?
 		exp = flt.bias
 	}
-	goto out
-
-overflow:
-	// ±Inf
-	mant = 0
-	exp = 1<<flt.expbits - 1 + flt.bias
-	overflow = true
-
-out:
 	// Assemble bits.
 	bits = mant & (uint64(1)<<flt.mantbits - 1)
 	bits |= uint64((exp-flt.bias)&(1<<flt.expbits-1)) << flt.mantbits

コアとなるコードの解説

変更されたコードは、floatBits 関数内で浮動小数点数の特殊な値(無限大と非正規化数)を処理するロジックを改善しています。

変更前:

変更前のコードでは、無限大の条件が満たされた場合に overflow ラベルにジャンプし、非正規化数の条件が満たされた場合に out ラベルにジャンプしていました。overflow ラベルでは無限大のビット表現を設定し、out ラベルは最終的なビットの組み立てに進むための共通の出口でした。この goto の使用は、コードの実行フローを非線形にし、特定の条件が満たされたときにどこにジャンプするのかを追跡する必要がありました。

 	// Infinities.
 	if exp-flt.bias >= 1<<flt.expbits-1 {
 		goto overflow // 無限大の場合、overflowラベルへジャンプ
 	}

 	// Denormalized?
 	if mant&(1<<flt.mantbits) == 0 {
 		exp = flt.bias // 非正規化数の場合、指数部を調整
 	}
 	goto out // 処理後、outラベルへジャンプ

overflow:
 	// ±Inf
 	mant = 0
 	exp = 1<<flt.expbits - 1 + flt.bias
 	overflow = true // 無限大のビット表現を設定
out:
 	// Assemble bits.
 	// ...

変更後:

変更後のコードでは、goto ステートメントが完全に削除され、代わりに if-else if 構造が導入されています。

 	// Infinities.
 	if exp-flt.bias >= 1<<flt.expbits-1 {
 		// ±Inf
 		mant = 0
 		exp = 1<<flt.expbits - 1 + flt.bias
 		overflow = true
 	} else if mant&(1<<flt.mantbits) == 0 {
 		// Denormalized?
 		exp = flt.bias
 	}
 	// Assemble bits.
 	// ...

この新しい構造では、以下のようになります。

  1. 無限大のチェック: if exp-flt.bias >= 1<<flt.expbits-1 で無限大の条件をチェックします。
    • この条件が真の場合、無限大のビット表現に必要な mant, exp, overflow の値を直接設定します。
    • このブロックが実行された場合、else if ブロックはスキップされます。
  2. 非正規化数のチェック: 最初の if 条件が偽の場合のみ、else if mant&(1<<flt.mantbits) == 0 で非正規化数の条件をチェックします。
    • この条件が真の場合、非正規化数に必要な exp の値を調整します。

この変更により、コードの実行フローがより明確で、上から下へと順に読み進めるだけでロジックを理解できるようになりました。特定の条件が満たされた場合にのみ、その条件に対応するコードブロックが実行されるため、コードの意図がより明確になり、保守性が向上します。goto に依存しないことで、将来的な変更やデバッグが容易になります。

関連リンク

参考にした情報源リンク

  • Go言語の goto ステートメントに関するドキュメントや議論
  • IEEE 754 浮動小数点数標準に関する情報(正規化数、非正規化数、無限大、NaN)
  • Go言語の strconv パッケージのドキュメント

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

このコミットは、Go言語の標準ライブラリである strconv パッケージ内の extfloat.go ファイルに対する変更です。具体的には、浮動小数点数のビット表現を扱う floatBits 関数から、不要な goto ステートメントを削除し、コードの可読性を向上させています。

コミット

commit 45b4867d0edabb5f2f8ee07d5f5e9b53f9f4dbe0
Author: Robin Eklind <r.eklind.87@gmail.com>
Date:   Wed Nov 14 09:42:48 2012 -0800

    strconv: Removed unnecessary use of goto. Made code easier to read.
    
    R=gri
    CC=gobot, golang-dev
    https://golang.org/cl/6855048

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

https://github.com/golang/go/commit/45b4867d0edabb5f2f8ee07d5f5e9b53f9f4dbe0

元コミット内容

strconv: Removed unnecessary use of goto. Made code easier to read.

このコミットは、strconv パッケージにおいて、不要な goto ステートメントを削除し、コードの可読性を向上させることを目的としています。

変更の背景

Go言語では、goto ステートメントの使用は推奨されていません。goto はプログラムの制御フローを複雑にし、コードの理解やデバッグを困難にする「スパゲッティコード」を生み出す原因となることがあります。このコミットは、既存のコードベースからこのような goto の使用を排除し、よりクリーンで保守しやすいコードに改善するという、一般的なソフトウェア開発のベストプラクティスに沿ったものです。特に、浮動小数点数の特殊なケース(無限大や非正規化数)を処理するロジックにおいて goto が使用されており、これをより構造化された if-else if 構造に置き換えることで、コードの意図が明確になり、エラーの発生しにくいコードへと改善されています。

前提知識の解説

Go言語における goto ステートメント

goto ステートメントは、プログラムの実行を指定されたラベルに無条件にジャンプさせる制御構造です。Go言語では goto の使用は可能ですが、その使用は非常に限定的であり、ほとんどのケースで for ループ、if/elseswitch などのより構造化された制御フローで代替可能です。goto は、ネストされたループからの脱出など、特定の状況でのみ考慮されるべきであり、一般的にはコードの可読性と保守性を低下させるため、避けるべきとされています。Goにおけるgotoの制限としては、同じ関数内のラベルにのみジャンプできること、新しい変数をスコープに持ち込むような変数宣言を飛び越えられないこと、ブロック内にジャンプできないことなどが挙げられます。

IEEE 754 浮動小数点数標準

このコミットが関連する extfloat.go ファイルは、浮動小数点数の内部表現を扱っています。浮動小数点数は、IEEE 754 標準に基づいて表現されます。この標準は、コンピュータが浮動小数点数をどのように格納し、演算するかを定義しています。

  • 正規化数 (Normalized Numbers): ほとんどの浮動小数点数は正規化数として表現されます。これは、仮数部が常に1で始まることを意味し、精度を最大化します。
  • 非正規化数 (Denormalized Numbers): 非常に小さい数を表現するために使用されます。これらの数は、仮数部が1で始まらないため、正規化数よりも精度が低い場合があります。アンダーフロー(非常に小さい数が0になること)を防ぐために重要です。
  • 無限大 (Infinities): 正の無限大 (+Inf) と負の無限大 (-Inf) があります。これは、オーバーフロー(非常に大きい数が表現範囲を超えること)や、0による除算などの結果として生じます。
  • NaN (Not a Number): 不定形な結果(例: 0/0、無限大/無限大)を表します。

floatBits 関数は、これらの特殊なケースを含む浮動小数点数を、そのビット表現に変換する役割を担っています。

strconv パッケージ

Go言語の strconv パッケージは、基本的なデータ型(文字列、整数、浮動小数点数、真偽値など)間の変換機能を提供します。例えば、文字列を整数に変換したり、浮動小数点数を文字列に変換したりする際に使用されます。このパッケージは、数値のパースやフォーマットにおいて非常に重要であり、その内部では数値のビット表現を正確に扱う必要があります。extfloat.go は、このパッケージの内部で、特に浮動小数点数の詳細なビット操作を行うために使用されるユーティリティファイルです。

技術的詳細

このコミットの技術的な詳細としては、floatBits 関数内の制御フローの変更が挙げられます。元のコードでは、無限大のケースと非正規化数のケースを処理するために goto overflowgoto out という2つの goto ステートメントが使用されていました。

  • if exp-flt.bias >= 1<<flt.expbits-1 { goto overflow }
    • これは、指数部が最大値に達した場合(無限大を表す場合)に overflow ラベルにジャンプしていました。
  • if mant&(1<<flt.mantbits) == 0 { exp = flt.bias } goto out
    • これは、仮数部が非正規化数であることを示す場合に指数部を調整し、その後 out ラベルにジャンプしていました。

新しいコードでは、これらの goto ステートメントが if-else if 構造に置き換えられています。

	if exp-flt.bias >= 1<<flt.expbits-1 {
		// ±Inf
		mant = 0
		exp = 1<<flt.expbits - 1 + flt.bias
		overflow = true
	} else if mant&(1<<flt.mantbits) == 0 {
		// Denormalized?
		exp = flt.bias
	}

この変更により、コードの実行パスが線形になり、特定の条件が満たされた場合にのみ特定の処理ブロックが実行されるという、より予測可能で理解しやすい流れになります。

  • まず、無限大の条件 (exp-flt.bias >= 1<<flt.expbits-1) をチェックします。この条件が真であれば、無限大の処理(mant を0に、exp を無限大の表現に設定し、overflowtrue にする)が実行されます。
  • もし最初の if 条件が偽であれば、else if ブロックに進み、非正規化数の条件 (mant&(1<<flt.mantbits) == 0) をチェックします。この条件が真であれば、非正規化数の処理(expflt.bias に設定する)が実行されます。

この構造は、各条件が独立して評価され、適切な処理が適用されることを保証します。goto を使用していた場合、コードを追う際にジャンプ先のラベルを探す必要がありましたが、if-else if ではブロックの範囲内で処理が完結するため、コードの局所性が高まり、理解が容易になります。

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

変更は src/pkg/strconv/extfloat.go ファイルの floatBits 関数内で行われています。

--- a/src/pkg/strconv/extfloat.go
+++ b/src/pkg/strconv/extfloat.go
@@ -152,22 +152,14 @@ func (f *extFloat) floatBits(flt *floatInfo) (bits uint64, overflow bool) {

 	// Infinities.
 	if exp-flt.bias >= 1<<flt.expbits-1 {
-		goto overflow
-	}

-	// Denormalized?
-	if mant&(1<<flt.mantbits) == 0 {
+		// ±Inf
+		mant = 0
+		exp = 1<<flt.expbits - 1 + flt.bias
+		overflow = true
+	} else if mant&(1<<flt.mantbits) == 0 {
+		// Denormalized?
 		exp = flt.bias
 	}
-	goto out
-
-overflow:
-	// ±Inf
-	mant = 0
-	exp = 1<<flt.expbits - 1 + flt.bias
-	overflow = true
-
-out:
 	// Assemble bits.
 	bits = mant & (uint64(1)<<flt.mantbits - 1)
 	bits |= uint64((exp-flt.bias)&(1<<flt.expbits-1)) << flt.mantbits

コアとなるコードの解説

変更されたコードは、floatBits 関数内で浮動小数点数の特殊な値(無限大と非正規化数)を処理するロジックを改善しています。

変更前:

変更前のコードでは、無限大の条件が満たされた場合に overflow ラベルにジャンプし、非正規化数の条件が満たされた場合に out ラベルにジャンプしていました。overflow ラベルでは無限大のビット表現を設定し、out ラベルは最終的なビットの組み立てに進むための共通の出口でした。この goto の使用は、コードの実行フローを非線形にし、特定の条件が満たされたときにどこにジャンプするのかを追跡する必要がありました。

 	// Infinities.
 	if exp-flt.bias >= 1<<flt.expbits-1 {
 		goto overflow // 無限大の場合、overflowラベルへジャンプ
 	}

 	// Denormalized?
 	if mant&(1<<flt.mantbits) == 0 {
 		exp = flt.bias // 非正規化数の場合、指数部を調整
 	}
 	goto out // 処理後、outラベルへジャンプ

overflow:
 	// ±Inf
 	mant = 0
 	exp = 1<<flt.expbits - 1 + flt.bias
 	overflow = true // 無限大のビット表現を設定
out:
 	// Assemble bits.
 	// ...

変更後:

変更後のコードでは、goto ステートメントが完全に削除され、代わりに if-else if 構造が導入されています。

 	// Infinities.
 	if exp-flt.bias >= 1<<flt.expbits-1 {
 		// ±Inf
 		mant = 0
 		exp = 1<<flt.expbits - 1 + flt.bias
 		overflow = true
 	} else if mant&(1<<flt.mantbits) == 0 {
 		// Denormalized?
 		exp = flt.bias
 	}
 	// Assemble bits.
 	// ...

この新しい構造では、以下のようになります。

  1. 無限大のチェック: if exp-flt.bias >= 1<<flt.expbits-1 で無限大の条件をチェックします。
    • この条件が真の場合、無限大のビット表現に必要な mant, exp, overflow の値を直接設定します。
    • このブロックが実行された場合、else if ブロックはスキップされます。
  2. 非正規化数のチェック: 最初の if 条件が偽の場合のみ、else if mant&(1<<flt.mantbits) == 0 で非正規化数の条件をチェックします。
    • この条件が真の場合、非正規化数に必要な exp の値を調整します。

この変更により、コードの実行フローがより明確で、上から下へと順に読み進めるだけでロジックを理解できるようになりました。特定の条件が満たされた場合にのみ、その条件に対応するコードブロックが実行されるため、コードの意図がより明確になり、保守性が向上します。goto に依存しないことで、将来的な変更やデバッグが容易になります。

関連リンク

参考にした情報源リンク

  • Go言語の goto ステートメントに関するドキュメントや議論
  • IEEE 754 浮動小数点数標準に関する情報(正規化数、非正規化数、無限大、NaN)
  • Go言語の strconv パッケージのドキュメント