[インデックス 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/else
、switch
などのより構造化された制御フローで代替可能です。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 overflow
と goto 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
を無限大の表現に設定し、overflow
をtrue
にする)が実行されます。 - もし最初の
if
条件が偽であれば、else if
ブロックに進み、非正規化数の条件 (mant&(1<<flt.mantbits) == 0
) をチェックします。この条件が真であれば、非正規化数の処理(exp
をflt.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.
// ...
この新しい構造では、以下のようになります。
- 無限大のチェック:
if exp-flt.bias >= 1<<flt.expbits-1
で無限大の条件をチェックします。- この条件が真の場合、無限大のビット表現に必要な
mant
,exp
,overflow
の値を直接設定します。 - このブロックが実行された場合、
else if
ブロックはスキップされます。
- この条件が真の場合、無限大のビット表現に必要な
- 非正規化数のチェック: 最初の
if
条件が偽の場合のみ、else if mant&(1<<flt.mantbits) == 0
で非正規化数の条件をチェックします。- この条件が真の場合、非正規化数に必要な
exp
の値を調整します。
- この条件が真の場合、非正規化数に必要な
この変更により、コードの実行フローがより明確で、上から下へと順に読み進めるだけでロジックを理解できるようになりました。特定の条件が満たされた場合にのみ、その条件に対応するコードブロックが実行されるため、コードの意図がより明確になり、保守性が向上します。goto
に依存しないことで、将来的な変更やデバッグが容易になります。
関連リンク
- Go CL 6855048: https://golang.org/cl/6855048
参考にした情報源リンク
- 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/else
、switch
などのより構造化された制御フローで代替可能です。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 overflow
と goto 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
を無限大の表現に設定し、overflow
をtrue
にする)が実行されます。 - もし最初の
if
条件が偽であれば、else if
ブロックに進み、非正規化数の条件 (mant&(1<<flt.mantbits) == 0
) をチェックします。この条件が真であれば、非正規化数の処理(exp
をflt.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.
// ...
この新しい構造では、以下のようになります。
- 無限大のチェック:
if exp-flt.bias >= 1<<flt.expbits-1
で無限大の条件をチェックします。- この条件が真の場合、無限大のビット表現に必要な
mant
,exp
,overflow
の値を直接設定します。 - このブロックが実行された場合、
else if
ブロックはスキップされます。
- この条件が真の場合、無限大のビット表現に必要な
- 非正規化数のチェック: 最初の
if
条件が偽の場合のみ、else if mant&(1<<flt.mantbits) == 0
で非正規化数の条件をチェックします。- この条件が真の場合、非正規化数に必要な
exp
の値を調整します。
- この条件が真の場合、非正規化数に必要な
この変更により、コードの実行フローがより明確で、上から下へと順に読み進めるだけでロジックを理解できるようになりました。特定の条件が満たされた場合にのみ、その条件に対応するコードブロックが実行されるため、コードの意図がより明確になり、保守性が向上します。goto
に依存しないことで、将来的な変更やデバッグが容易になります。
関連リンク
- Go CL 6855048: https://golang.org/cl/6855048
参考にした情報源リンク
- Go言語の
goto
ステートメントに関するドキュメントや議論 - IEEE 754 浮動小数点数標準に関する情報(正規化数、非正規化数、無限大、NaN)
- Go言語の
strconv
パッケージのドキュメント