JSON::XSの問題が再現しない環境に関するメモ

http://d.hatena.ne.jp/hiratara/20121024/1351054828
Floating point number decoding in JSON::XS - blog.64p.org

の問題が再現しない環境に関するメモ

環境

i386 LinuxWindows上の Unix環境(Cygwin等)ではおそらく再現しない
のではないかと思います。利用した JSON::XSはそれぞれ 2.33です。

#!perl
use strict;
use warnings;

use JSON::XS qw(decode_json);

my ($dat) = @{decode_json("[0.6]")};

printf("%.20f\n", $dat);
printf("%.20f\n", 0.6);
Ubuntu 10.04 i386の結果
0.59999999999999997780
0.59999999999999997780
Ubuntu 12.04 x86_64の結果
0.60000000000000008882
0.59999999999999997780

一致しません

i386 Linuxで再現させる方法

一旦 "perl Makefile.PL"をして Makefileに以下のパッチを当てて
JSON::XSを構築しなおし、インストールします。

diff --git a/Makefile b/Makefile
index 4dc15c0..49f77b3 100644
--- a/Makefile
+++ b/Makefile
@@ -277,7 +277,7 @@ DISTVNAME = JSON-XS-2.33
 
 # --- MakeMaker cflags section:
 
-CCFLAGS = -fno-strict-aliasing -pipe -fstack-protector -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64
+CCFLAGS = -fno-strict-aliasing -pipe -fstack-protector -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -mfpmath=sse -msse2
 OPTIMIZE = -O2
 PERLTYPE = 
 MPOLLUTE =  


これで上記のテストコードを実行すると以下の結果が得られます。

0.60000000000000008882
0.59999999999999997780


期待する結果が得られました。

原因

原因は拡張浮動小数点数に関わる問題と考えられます。詳しい説明は
以下が詳しいので気になる方は参照してみてください。


自前の atof関数だけ抜き出してみて再現するか確認してみた
ところそれだと fpmathを返るまでもなく異なる結果となって
しまいました。JSON::XS内ではうまい具合に 80bitのまま
データが保持され、たまたまうまくいっていたということに
なるのでしょうか。詳しいことはわかりませんが・・・。

おわりに

数値計算コンパイラ関係者はこのあたりを熟知しているので
近くにそういう人がいればいろいろ教えてくれると思います。
とりあえずはデフォルトでは i386環境だと倍精度浮動小数点は
演算途中で 80bit精度になっているということと、x86_64,
MacOSX環境では倍精度浮動小数点数は常に 64bitで演算されている
ということを頭に入れておけばよいでしょう。


早く治るんでしょうか、この問題は・・・。