[インデックス 1082] ファイルの概要
このコミットは、Go言語の標準ライブラリにおいて、ASCII文字列を浮動小数点数に変換する基本的な機能(atof64
, atof
, atof32
)を導入し、既存の浮動小数点数を文字列に変換する関数(dtoa
をf64toa
にリネーム)を整理するものです。特に、文字列から浮動小数点数への変換は「非常に弱い (VERY WEAK)」実装であると明記されており、初期段階のシンプルな実装であることが示唆されています。
コミット
commit 175dd773e6808d755e276c13427c7d448dfa29a7
Author: Rob Pike <r@golang.org>
Date: Thu Nov 6 16:32:28 2008 -0800
simpleminded ascii to floating point conversion
R=rsc
DELTA=111 (107 added, 0 deleted, 4 changed)
OCL=18720
CL=18725
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/175dd773e6808d755e276c13427c7d448dfa29a7
元コミット内容
simpleminded ascii to floating point conversion
R=rsc
DELTA=111 (107 added, 0 deleted, 4 changed)
OCL=18720
CL=18725
変更の背景
このコミットは、Go言語がまだ開発の初期段階にあった2008年11月に行われたものです。当時のGo言語には、文字列から浮動小数点数への変換を行う標準的なユーティリティ関数が不足していました。アプリケーションが外部からの入力(設定ファイル、ユーザー入力など)を数値として処理するためには、このような変換機能が不可欠です。
コミットメッセージにある「simpleminded」やコード内の「THIS CODE IS VERY WEAK」というコメントから、これは完全な実装ではなく、まずは基本的な機能を提供し、後でより堅牢で高性能な実装に置き換えることを意図した、暫定的な措置であったと考えられます。Go言語の設計哲学の一つに「実用性」があり、まずは動くものを提供し、徐々に改善していくアプローチが取られていたことが伺えます。
また、dtoa
からf64toa
へのリネームは、関数名がより具体的になり、float64
型に特化した変換であることを明確にするための変更と推測されます。これは、Go言語の型システムと命名規則の進化の一環である可能性があります。
前提知識の解説
浮動小数点数 (Floating-Point Numbers)
浮動小数点数は、非常に大きい数や非常に小さい数を表現するためにコンピュータで用いられる数値表現形式です。一般的にはIEEE 754標準に基づいており、符号部、指数部、仮数部から構成されます。Go言語ではfloat32
(単精度)とfloat64
(倍精度)の2つの浮動小数点型があります。
ASCIIから浮動小数点数への変換 (ASCII to Floating-Point Conversion)
文字列(ASCII表現)を浮動小数点数に変換するプロセスは、通常、以下のステップを含みます。
- 符号の解析: 文字列の先頭にある
+
または-
を識別し、数値の符号を決定します。 - 整数部の解析: 小数点より前の数字列を整数として解析します。
- 小数部の解析: 小数点より後の数字列を解析し、その値を10の負のべき乗でスケーリングして加算します。
- 指数部の解析:
e
またはE
に続く指数部(例:1.23e+4
の+4
)を解析し、最終的な数値を10のそのべき乗で乗算または除算します。
このプロセスは、数値の精度、丸め処理、エラーハンドリング(無効な文字、オーバーフロー、アンダーフローなど)を考慮に入れると非常に複雑になります。
Go言語のreflect
パッケージとstrings
パッケージ
reflect
パッケージ: Go言語のreflect
パッケージは、実行時にプログラムの構造を検査・操作するための機能を提供します。このコミットでは、reflect/tostring.go
がstrings.dtoa
からstrings.f64toa
への関数名変更に対応しています。これは、リフレクションを通じて値の文字列表現を取得する際に、適切な浮動小数点数変換関数を呼び出すための変更です。strings
パッケージ:strings
パッケージは、文字列操作のためのユーティリティ関数を提供します。このコミットの主要な変更は、このパッケージに文字列から浮動小数点数への変換機能を追加することです。
技術的詳細
このコミットの主要な技術的変更は、src/lib/strings.go
にatof64
、atof
、atof32
という3つの新しい関数が追加されたことです。これらは、文字列をそれぞれfloat64
、float
(当時のGo言語におけるデフォルトの浮動小数点型、通常はfloat64
)、float32
に変換します。
atof64
関数の実装概要
atof64
関数は、文字列をfloat64
に変換する中心的なロジックを含んでいます。その実装は、以下のステップで構成されます。
- 空文字列のチェック: 入力文字列が空の場合、
false
を返してエラーとします。 - 符号の処理:
- 文字列の先頭が
+
または-
であるかをチェックします。 -
であればneg
フラグをtrue
に設定し、符号文字をスキップします。+
であればそのままスキップします。
- 文字列の先頭が
- 整数部の解析:
- 小数点(
.
)または指数表記(e
,E
)が現れるまで、数字を読み込み、整数値n
を構築します。 - 数字以外の文字が現れた場合、
false
を返してエラーとします。 result
にfloat64(n)
を代入します。
- 小数点(
- 小数部の解析:
- 現在の文字が小数点(
.
)の場合、小数点以下を解析します。 frac
(小数部の整数値)とscale
(小数部の桁数に応じた10のべき乗)を計算します。result
にfloat64(frac)/scale
を加算します。- 数字以外の文字が現れた場合、
false
を返してエラーとします。
- 現在の文字が小数点(
- 指数部の解析:
- 現在の文字が
e
またはE
の場合、指数部を解析します。 - 指数部の符号(
+
または-
)を処理します。 - 指数値
exp
を読み込みます。 exp
の値に応じて、result
を10のべき乗で乗算または除算します。- 数字以外の文字が現れた場合、
false
を返してエラーとします。
- 現在の文字が
- 最終的な符号の適用:
neg
フラグがtrue
であれば、result
を負の値にします。 - 結果の返却: 変換された
float64
値とtrue
(成功)を返します。
atof
とatof32
関数
これらの関数は、内部的にatof64
を呼び出し、その結果をそれぞれfloat
型またはfloat32
型にキャストして返します。これは、Go言語における型変換の一般的なパターンです。
dtoa
からf64toa
へのリネーム
src/lib/strings.go
では、既存のdtoa
関数がf64toa
にリネームされています。これは、float64
型を文字列に変換する関数であることをより明確にするための変更です。src/lib/reflect/tostring.go
もこの変更に合わせて更新されています。
既知の弱点
コミットメッセージとコード内のコメントで「THIS CODE IS VERY WEAK.」と明記されているように、この実装にはいくつかの既知の弱点があります。
- 不完全なエラーハンドリング: 「
.
」や「e4
」、「1e-
」のような不完全または無効な形式の文字列を許容してしまう可能性があります。これは、厳密な数値解析においては問題となります。 - 精度と丸め: 浮動小数点数の変換において、精度や丸め処理に関する考慮が不十分である可能性があります。特に、非常に長い小数部や指数部を持つ数値の場合、正確な変換が保証されないことがあります。
- パフォーマンス: 文字列を1文字ずつ解析し、ループ内で乗算や除算を行うシンプルな実装であるため、大規模なデータセットや高性能が求められる場面では、より最適化されたアルゴリズム(例: Dragonboxアルゴリズムなど)に比べてパフォーマンスが劣る可能性があります。
これらの弱点は、この機能が「simpleminded」な初期実装であり、将来的に改善されるべき課題として認識されていたことを示しています。
コアとなるコードの変更箇所
src/lib/reflect/tostring.go
--- a/src/lib/reflect/tostring.go
+++ b/src/lib/reflect/tostring.go
@@ -124,7 +124,7 @@ func integer(v int64) string {
}
func floatingpoint(v float64) string {
- return strings.dtoa(v);
+ return strings.f64toa(v);
}
func ValueToString(val Value) string {
floatingpoint
関数内でstrings.dtoa
の呼び出しがstrings.f64toa
に変更されています。
src/lib/strings.go
--- a/src/lib/strings.go
+++ b/src/lib/strings.go
@@ -220,7 +220,7 @@ export func itoa(i int) string {
// Convert float64 to string. No control over format.
// Result not great; only useful for simple debugging.
-export func dtoa(v float64) string {
+export func f64toa(v float64) string {
var buf [20]byte;
const n = 7; // digits printed
@@ -280,5 +280,107 @@ export func dtoa(v float64) string {
}
export func ftoa(v float) string {
- return dtoa(float64(v));
+ return f64toa(float64(v));
+}
+
+export func f32toa(v float32) string {
+ return f64toa(float64(v));
+}
+
+// Simple conversion of string to floating point.
+// TODO: make much better. THIS CODE IS VERY WEAK.
+// Lets through some poor cases such as "." and "e4" and "1e-". Fine.
+export func atof64(s string) (f float64, ok bool) {
+ // empty string bad
+ if len(s) == 0 {
+ return 0, false
+ }
+
+ // pick off leading sign
+ neg := false;
+ if s[0] == '+' {
+ s = s[1:len(s)]
+ } else if s[0] == '-' {
+ neg = true;
+ s = s[1:len(s)]
+ }
+
+ // parse number
+ // first, left of the decimal point.
+ n := uint64(0);
+ i := 0;
+ for ; i < len(s); i++ {
+ if s[i] == '.' || s[i] == 'e' || s[i] == 'E' {
+ break
+ }
+ if s[i] < '0' || s[i] > '9' {
+ return 0, false
+ }
+ n = n*10 + uint64(s[i] - '0')
+ }
+ result := float64(n);
+ if i != len(s) {
+ frac := uint64(0);
+ scale := float64(1);
+ // decimal and fraction
+ if s[i] == '.' {
+ i++;
+ for ; i < len(s); i++ {
+ if s[i] == 'e' || s[i] == 'E' {
+ break
+ }
+ if s[i] < '0' || s[i] > '9' {
+ return 0, false
+ }
+ frac = frac*10 + uint64(s[i] - '0');
+ scale = scale * 10.0;
+ }
+ }
+ result += float64(frac)/scale;
+ // exponent
+ if i != len(s) { // must be 'e' or 'E'
+ i++;
+ eneg := false;
+ if i < len(s) && s[i] == '-' {
+ eneg = true;
+ i++;
+ } else if i < len(s) && s[i] == '+' {
+ i++;
+ }
+ // this works ok for "1e+" - fine.
+ exp := uint64(0);
+ for ; i < len(s); i++ {
+ if s[i] < '0' || s[i] > '9' {
+ return 0, false
+ }
+ exp = exp*10 + uint64(s[i] - '0');
+ }
+ if eneg {
+ for exp > 0 {
+ result /= 10.0;
+ exp--;
+ }
+ } else {
+ for exp > 0 {
+ result *= 10.0;
+ exp--;
+ }
+ }
+ }
+ }
+
+ if neg {
+ result = -result
+ }
+ return result, true
+}
+
+export func atof(s string) (f float, ok bool) {
+ a, b := atof64(s);
+ return float(a), b;
+}
+
+export func atof32(s string) (f float32, ok bool) {
+ a, b := atof64(s);
+ return float32(a), b;
}
dtoa
関数がf64toa
にリネームされ、atof64
、atof
、atof32
の各関数が新規追加されています。
test/stringslib.go
--- a/test/stringslib.go
+++ b/test/stringslib.go
@@ -109,4 +109,9 @@ func main() {
// should work if int == int64: is there some way to know?
// if itoa(-1<<63) != "-9223372036854775808" { panic("itoa 1<<63") }\n+\n+\t{\n+\t\ta, ok := strings.atof64("-1.2345e4");\n+\t\tif !ok || a != -12345. { panic(a, "atof64 -1.2345e4") }\n+\t}\n }
strings.atof64
の簡単なテストケースが追加されています。-1.2345e4
が-12345.0
に正しく変換されるかを確認しています。
コアとなるコードの解説
このコミットの核心は、src/lib/strings.go
に追加されたatof64
関数です。この関数は、文字列を解析して浮動小数点数に変換する基本的なロジックを実装しています。
f64toa
へのリネーム: 既存のdtoa
関数は、float64
を文字列に変換する役割を担っていましたが、より明確な命名としてf64toa
にリネームされました。これにより、関数の目的と対象となる型が直感的に理解できるようになりました。atof64
の実装:- この関数は、文字列の先頭から順に、符号、整数部、小数部、指数部を解析していきます。
- 各部分の解析は、文字が数字であるか、小数点、または指数記号であるかを確認することで行われます。
- 整数部と小数部は、
uint64
として読み込まれ、その後float64
に変換されます。小数部はscale
変数を使って適切な桁に調整されます。 - 指数部は、
exp
変数に読み込まれ、ループを使ってresult
を10のべき乗で乗算または除算することで適用されます。 - エラーハンドリングは非常にシンプルで、無効な文字が見つかった場合や空文字列の場合に
false
を返します。
atof
とatof32
: これらはatof64
のラッパー関数であり、Go言語の型システムに合わせて、より汎用的なfloat
型やfloat32
型への変換を提供します。これにより、ユーザーは特定の浮動小数点型を意識せずに変換関数を呼び出すことができます。- テストケース:
test/stringslib.go
に追加されたテストケースは、atof64
が負の数と指数表記を含む文字列を正しく解析できることを検証しています。これは、基本的な機能が動作することを確認するための最小限のテストです。
この実装は、Go言語の初期段階において、必要最低限の機能を提供することを目的としたものであり、その後のGo言語の進化とともに、より堅牢で高機能なstrconv
パッケージのParseFloat
関数などに置き換えられていくことになります。
関連リンク
- Go言語の公式ドキュメント: https://go.dev/
- Go言語の
strconv
パッケージ(現在の文字列と数値の変換を扱う標準パッケージ): https://pkg.go.dev/strconv
参考にした情報源リンク
- Go言語のコミット履歴 (GitHub): https://github.com/golang/go/commits/master
- IEEE 754 浮動小数点数標準に関する情報 (Wikipediaなど): https://ja.wikipedia.org/wiki/IEEE_754
- Go言語の初期開発に関する議論やメーリングリストのアーカイブ(もしあれば)