python2.6から2.7になってfuncparserlibのテストがコケる。

公式パッケージになった際には問題なかったのだが、登録されたBTSのメールを見て、自分の環境でもdebuildを実行してみたら同様にコケた。ビルドログは こちら

make[2]: ディレクトリ `/home/kohei/devel/debpkg/funcparserlib/funcparserlib-0.3.5/examples/json' に入ります
../run-tests
test-1-array ok
test-1-object ok
test-bool-and-null ok
test-empty-array ok
test-empty-object ok
test-many-array ok
test-many-object ok
test-null ok
test-numbers
--- test-numbers.out    2009-07-17 09:13:39.000000000 +0900
+++ /tmp/tmp5HRA_i      2011-10-27 00:55:08.000000000 +0900
@@ -5,9 +5,9 @@
  -14,
  65536,
  0.0,
- 3.1400000000000001,
- -3.1400000000000001,
+ 3.14,
+ -3.14,
  -123.456,
- 6.6742799999999995e-11,
+ 6.67428e-11,
  -1.602176e-19,
- 6.6742799999999995e-11]
+ 6.67428e-11]



fail
test-strings ok
test-toplevel-string ok
--- stats ---
fail 1 ok 10 excpt 0
make[2]: *** [test] エラー 1
make[2]: ディレクトリ `/home/kohei/devel/debpkg/funcparserlib/funcparserlib-0.3.5/examples/json' から出ます
make[1]: *** [override_dh_auto_test] エラー 2
make[1]: ディレクトリ `/home/kohei/devel/debpkg/funcparserlib/funcparserlib-0.3.5' から出ます
make: *** [build] エラー 2
dpkg-buildpackage: error: debian/rules build gave error exit status 2
debuild: fatal error at line 1348:
dpkg-buildpackage -rfakeroot -D -us -uc failed

コケているtest-numbersのコードはこれ。

#!/bin/sh
exec python json.py <<END
[
    0, 1, -1, 14, -14, 65536,
    0.0, 3.14, -3.14, -123.456,
    6.67428e-11, -1.602176e-19, 6.67428E-11
]
END

この1ヶ月で大きく変わったことと言えば、Pythonのデフォルトのバージョンが2.6から2.7になったことくらいだろうと思い、”exec python json.py”の部分を、python2.6に変えて実行してみた。

$ ../run-tests
test-1-array ok
test-1-object ok
test-bool-and-null ok
test-empty-array ok
test-empty-object ok
test-many-array ok
test-many-object ok
test-null ok
test-numbers ok
test-strings ok
test-toplevel-string ok
--- stats ---
fail 0 ok 11 excpt 0

予想通り、問題無し。

python2.6でtest-numbersを手動で組み込みコマンドのreprで表示させてみるとこんな感じになる。

$ python2.6
Python 2.6.7 (r267:88850, Aug  3 2011, 11:33:52)
[GCC 4.6.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> repr(0);repr(1);repr(-1);repr(14);repr(-14);repr(65536);repr(0.0)
'0'
'1'
'-1'
'14'
'-14'
'65536'
'0.0'

python2.7だと次のとおり。

$ python
Python 2.7.2+ (default, Oct  5 2011, 10:41:47)
[GCC 4.6.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> repr(0);repr(1);repr(-1);repr(14);repr(-14);repr(65536);repr(0.0)
'0'
'1'
'-1'
'14'
'-14'
'65536'
'0.0'

ここまでは差異無し。浮動小数点で違いがはっきり。

python2.6

>>> repr(3.14);repr(-3.14);repr(-123.456);repr(6.67428e-11);repr(-1.602176e-19);repr(6.67428E-11)
'3.1400000000000001'
'-3.1400000000000001'
'-123.456'
'6.6742799999999995e-11'
'-1.602176e-19'
'6.6742799999999995e-11'

python2.7

>>> repr(3.14);repr(-3.14);repr(-123.456);repr(6.67428e-11);repr(-1.602176e-19);repr(6.67428E-11)
'3.14'
'-3.14'
'-123.456'
'6.67428e-11'
'-1.602176e-19'
'6.67428e-11'

先ほどのテストの結果のとおりですね。2進数で割りきれない数字が、 2.6と2.7の浮動小数点の扱いが変わったのがの原因で、2.7から丸められるようになった ためのようです。

2.6系と同じ結果にするには、下記のようにすれば良いようです。

>>> from decimal import Decimal
>>> format(Decimal.from_float(3.14), '.16f')
'3.1400000000000001'
>>> format(Decimal.from_float(-3.14), '.16f')
'-3.1400000000000001'
>>> format(Decimal.from_float(6.67428e-11), '.16e')
'6.6742799999999995e-11'
>>> format(Decimal.from_float(6.67428E-11), '.16e')
'6.6742799999999995e-11'

でも、2.7ではなく2.6に合わせるというのではそもそも問題は解決しないので、さて、どうしたもんかなぁ。