LoginSignup
3
3

More than 3 years have passed since last update.

go で float32 を JSON にするとちょっと意外なことになる

Last updated at Posted at 2019-09-26

まあタイトルのとおり。
ちょっと意外なことがあったので。

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 をわざわざ使っているんだからなんか理由があるに違いなくて、簡単にはやめられないよね。
どうしたものか。

追記

というわけで、ライブラリ書いた。

see https://github.com/nabetani/marshaljsonf64

3
3
12

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3