NaNの動き
ふと気になって調べてみました。
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
なんと、trueです。
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となるようです。