まあタイトルのとおり。
ちょっと意外なことがあったので。
go の場合
まずはソースコード:
package main
import (
"encoding/json"
"fmt"
)
type Hoge struct {
Foo float32
Bar float64
}
func main() {
hoge := Hoge{Foo: 0xa0000000, Bar: 0xa0000000}
j, _ := json.Marshal(hoge)
fmt.Println(string(j))
}
実行すると、出力は以下のようになる:
{"Foo":2684354600,"Bar":2684354560}
ぱっとみわかりにくいけれど、 float32 に由来する Foo は下二桁が 00
なのに対し、float64 に由来する Bar は、下二桁が 60
になっている。
同じ 0xa0000000
を入れているのに違う値になる。困る。
C# の場合
こちらもソースコードから。
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
[DataContract]
public class Hoge
{
[DataMember]
public float foo { get; set; }
[DataMember]
public double bar { get; set; }
}
internal class JsonSerializer
{
public static DataContractJsonSerializer Serializer<t>()
{
System.Type type = typeof(t);
return new DataContractJsonSerializer(type);
}
}
partial class Program
{
static void Main(string[] args)
{
var t = new Hoge();
t.foo = 0xa0000000L;
t.bar = 0xa0000000L;
using (var fs = System.Console.OpenStandardOutput())
{
JsonSerializer.Serializer<Hoge>().WriteObject(fs, t);
}
}
}
結果は次の通り:
{"bar":2684354560,"foo":2.68435456E+09}
出力する書式は異なるものの、実質的に同じ値になっている。
これなら困らない。
Java + Gson の場合
やっぱりソースコードから。
import com.google.gson.Gson;
class Hoge {
public float foo;
public double bar;
}
public class Main {
public static void main(String[] args) {
Hoge hoge = new Hoge();
hoge.foo = 0xa0000000L;
hoge.bar = 0xa0000000L;
Gson gson = new Gson();
System.out.println(gson.toJson(hoge));
}
}
Java には標準となる JSON Serializer がない模様なので Gson を使ってみた。
出力結果は:
{"foo":2.68435456E9,"bar":2.68435456E9}
出力形式も値も同じ。
困らない。
node.js の場合
ここでもまずはソースコード:
o = {
foo: new Float32Array([0xa0000000]),
bar: new Float64Array([0xa0000000])
}
process.stdout.write(JSON.stringify(o));
出力はこうなる:
{"foo":{"0":2684354560},"bar":{"0":2684354560}}
Float32Array とかって配列にならないんだへー、みたいな気持ちにはなるが、Float32Array でも Float64Array でもおなじになっている。
まとめ
32bit および 64bit の浮動小数点型変数に 0xa0000000
を入れて、それを JSON にしてみた。
結果は下表の通り。
言語など | 型 | JSON内の表現 | JSON内の表現を16進数で表現したもの |
---|---|---|---|
go | float32 | 2684354600 |
0xa0000028 |
go | float64 | 2684354560 |
0xa0000000 |
C# | float | 2.68435456E+09 |
0xa0000000 |
C# | double | 2684354560 |
0xa0000000 |
Java+Gson | float | 2.68435456E9 |
0xa0000000 |
Java+Gson | double | 2.68435456E9 |
0xa0000000 |
node.js | Float32Array | 2684354560 |
0xa0000000 |
node.js | Float64Array | 2684354560 |
0xa0000000 |
go で float32 型の値を JSON にした場合だけ、0xa0000000
にならない。
困る。
対策は「float32 を使わない」なんだけど、float32 をわざわざ使っているんだからなんか理由があるに違いなくて、簡単にはやめられないよね。
どうしたものか。
追記
というわけで、ライブラリ書いた。