Help us understand the problem. What is going on with this article?

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

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

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

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした