quabbinの日記: [Ruby] NaNの動き 2
Ruby も NaN == NaN が true になる可能性が…
Twitter / 星一: Ruby も NaN == NaN が true に ...
ふと気になって調べてみました。
1.4~1.9系Rubyでの動き
NaNはFloatの演算で算出することが出来ます。
つまり、
0.0/0
がNaNになるはずなので、
p 0.0/0 == 0.0/0
がtrueになるかfalseになるかを観察する方向でチェックできるはずです。
そこで手元にあったActiveScriptRubyの1.4~1.8でチェックしてみます。
C:\Users\quabbin>\Develop\Language\Ruby-1.4\ruby.exe -ve 'p 0.0/0 == 0.0/0'
ruby 1.4.6 (2000-08-16) [i386-mswin32]
true
C:\Users\quabbin>\Develop\Language\Ruby-1.6.8.3\bin\ruby.exe -ve 'p 0.0/0 == 0.0
/0'
ruby 1.6.8 (2002-12-24) [i586-mswin32]
true
C:\Users\quabbin>\Develop\Language\Ruby-1.8\bin\ruby.exe -ve 'p 0.0/0 == 0.0/0'
ruby 1.8.7 (2008-08-11 patchlevel 72) [i386-mswin32]
false
1.6以前は軒並みtrue、1.8からはfalseになりました。
さすがに1.2以前のコードはないからチェックできませんので、それ以前に関しては不明ですが。
# SVN上には1.1や1.0がありますが、コンパイルするのは大変そうです。
ここでcygwinに移って1.9を試してみます
quabbin@Litchi ~ $ ruby -ve 'p 0.0/0 == 0.0/0'
ruby 1.8.7 (2008-08-11 patchlevel 72) [i386-cygwin]
false
quabbin@Litchi ~ $ ruby1.9 -ve 'p 0.0/0 == 0.0/0'
ruby 1.9.1p129 (2009-05-12 revision 23412) [i386-cygwin]
false
1,8と1.9を試すこととなったわけですが、やはり普通にfalseとなりました。
ちなみに、ruby1.9は、1.9系rubyへのシンボリックリンクです。一応。
同一変数だとどうなる?
ここではたと、同一の変数で比較したならどうなるか気になりました。
quabbin@Litchi ~ $ ruby1.9 -e 'p nan = 0.0/0 && nan == nan'
true
</eocde>
なんと、trueです。
<ecode>
quabbin@Litchi ~ $ ruby1.9 -e 'p [(0.0/0).object_id, (0.0/0).object_id]'
[136653180, 136653170]
object_idを比べると、NaNは発生するごとにインスタンスが生成されていることが分かります。
インスタンスが別ならfalseで、インスタンスが同一ならtrueとなると、まるで==メソッドが定義されてないクラスのインスタンスみたいな振る舞いです。
quabbin@Litchi ~ $ ruby1.9 -ve 'p nan = 0.0/0 && nan == 0.0/0'
ruby 1.9.1p129 (2009-05-12 revision 23412) [i386-cygwin]
false
quabbin@Litchi ~ $ ruby1.9 --dump=insns -ve 'p nan = 0.0/0 && nan == 0.0/0'
ruby 1.9.1p129 (2009-05-12 revision 23412) [i386-cygwin]
== disasm: <RubyVM::InstructionSequence:<main>@-e>======================
local table (size: 2, argc: 0 [opts: 0, rest: -1, post: 0, block: -1] s1)
[ 2] nan
0000 trace 1 ( 1)
0002 putnil
0003 putobject 0.0
0005 putobject 0
0007 opt_div
0008 dup
0009 branchunless 22
0011 pop
0012 getdynamic nan, 0
0015 putobject 0.0
0017 putobject 0
0019 opt_div
0020 opt_eq <ic>
0022 dup
0023 setdynamic nan, 0
0026 send :p, 1, nil, 8, <ic>
0032 leave
quabbin@Litchi ~ $ ruby1.9 --dump=insns -ve 'p nan = 0.0/0 && nan == nan'
ruby 1.9.1p129 (2009-05-12 revision 23412) [i386-cygwin]
== disasm: <RubyVM::InstructionSequence:<main>@-e>======================
local table (size: 2, argc: 0 [opts: 0, rest: -1, post: 0, block: -1] s1)
[ 2] nan
0000 trace 1 ( 1)
0002 putnil
0003 putobject 0.0
0005 putobject 0
0007 opt_div
0008 dup
0009 branchunless 20
0011 pop
0012 getdynamic nan, 0
0015 getdynamic nan, 0
0018 opt_eq <ic>
0020 dup
0021 setdynamic nan, 0
0024 send :p, 1, nil, 8, <ic>
0030 leave
上で使ったコードを代入の形にし、期待通り動くことを見てからコードのパース状態を出力させます。
一つ目のコードでは0.0/0が二回実行されているのに対し、二つ目のコードでは一回しか実行されてません。
つまり、見た目の相違以上の相違が見えません。
更にCレベルのソースを追ってみたのですが、いまひとつ原因が追いきれず、今日はここで時間切れ。
また後日とします。
# Cレベルのデバッグ方法について、あまり詳しくないのが敗因…。
ちなみにpythonでは…
Rubyとよく比較されるPythonでは、このあたりどうなっているのでしょう。
動きを見てみます。
quabbin@Litchi ~ $ python --version
Python 2.5.2
quabbin@Litchi ~ $ python -c 'n = float("NaN");print n == n'
False
quabbin@Litchi ~ $ python -c 'print id(float("NaN")), id(float("NaN"))'
17463392 17463392
quabbin@Litchi ~ $ python -c '0.0 / 0.0'
Traceback (most recent call last):
File "<string>", line 1, in <module>
ZeroDivisionError: float division
quabbin@Litchi ~ $ python -c 'float("0.0") / float("0.0")'
Traceback (most recent call last):
File "<string>", line 1, in <module>
ZeroDivisionError: float division
# あまりPythonに詳しくないので、これであっているかは自信がありません。
Pythonでは0除算すると0除算例外が発生してしまうようです。
また興味深いことに、NaNはSingletonなインスタンスが発生するようで、==で比較すると常にFlaseとなるようです。
NaNのまわりは、結構言語によって扱いが違うのですね。
ところでこの問題、数学的にはどう動くのが正しいのでしょう。
数学が詳しければ、いろいろと判断付くのでしょう。
…しっかり数学を勉強したい今日この頃です。
07/07 01:08追記
elderwandさんの指摘を参考に試してみたところ、Pythonでもインスタンスを複数生成できるが、比較してもFalseとなるようです。
NaN in Python (スコア:1)
Python では、inf/inf で nan が得られるみたいですね。
>>> 1e309
inf
>>> 1e309/1e309
nan
#その先は、私にはわかりません。
Re:NaN in Python (スコア:2)
なるほど。
ちょっとやってみました。
infでやるとオブジェクトは別々に出来ると。
float()でnanを作っているから、同じものが返ってきたというだけなのですね。
訂正します。
ありがとうございました。