[Go] json.Marshalのfloat値の文字列表現

float64の値をfmt.Sprintf("%f",v)で文字列にすると、小数点以下6桁までしか出力されない。
出力する桁数を指定するには、fmt.Sprintf(".8f",v)のように".<桁数>"で指定する。

この指定方法を知った時に、json.Marshalのフォーマットはどうなっているのかが気になって試してみた。


v1 := float64(1.23456789)
	v2 := float64(0.00000089)
	v3 := float64(123456789012345678901)
	v4 := float64(1234567890123456789012)
	fmt.Printf("%f\n", v1)
	fmt.Printf("%.8f\n", v1)
	m := make(map[string]float64)
	m["v1"] = v1
	m["v2"] = v2
	m["v3"] = v3
	m["v4"] = v4
	b, _ := json.Marshal(m)
	fmt.Println(string(b))
実行結果。

1.234568
1.23456789
{"v1":1.23456789,"v2":8.9e-7,"v3":123456789012345680000,"v4":1.2345678901234568e+21}

https://play.golang.org/p/ilRSGeVF82R

なぜこうなるのかがよくわからなかったので、Marshalの中身を確認してみた。
結果としては、floatEncoderの以下の個所で、値の大きさを判定して出力フォーマットを切り替えている。


if abs != 0 {
		if bits == 64 && (abs < 1e-6 || abs >= 1e21) || bits == 32 && (float32(abs) < 1e-6 || float32(abs) >= 1e21) {
			fmt = 'e'
		}
	}
	b = strconv.AppendFloat(b, f, fmt, -1, int(bits))


小さい数と大きい数を指数表記にしているということはわかるんだけど、切替の基準がわからず。
と思ったら少し上にコメントで記載されていた。


	// Convert as if by ES6 number to string conversion.
	// This matches most other JSON generators.


ES6の数値→文字列変換の仕様にあわせたものらしい。
JSONがjavascript object notationであることを考えると、javascriptの仕様にそった文字列表現にするのは当たり前なのか。

当然なんだけど、ちゃんと考えられてるんだなと。


この記事が気に入ったらサポートをしてみませんか?