[Ruby] NaN。結論はまた今度

Ruby側でNaNが同一視されない問題について、どこをどうすれば他の言語と同じ動きになるか分かったので、先行論議がないか調べてみました。
すると
Ruby 1.9 - Bug #1720: [NaN] == [NaN] が true になる - Ruby Issue Tracking Systemを発見。
日付は2009/7/3。
あ〜。つまるところ、乗り遅れなわけですねorz


内容を見てみると、


(1) NaN == NaN も true にする
一貫性はあるが NaN の本来の挙動ではない
(2) rb_equal()でまずequal?でのチェックをやめる
性能が劣化するので避けたい
(3) rb_equal()でT_FLOATを特別扱い
2ほどではないにしても性能劣化が気になる
特別扱いは後悔することが多い
(4) このまま。これは例外的なケースとする
で、4に傾きそうな感じ。
ちなみに私は3の案を考えてましたが、パフォーマンスに引っかかりを覚えるだろうなと思っていたら予想通りでした。


一つ前のエントリーに、ACさんttさんからコメントを頂きました。


ACさんの指摘。

SQL では null 比較を = ではなく is や is not で行うというところにも目を向けると面白いかもしれません。
が興味深い。
手元のMySQLで試してみます

quabbin@itsumi:~$ mysql -u root -p
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 52
Server version: 5.1.31-1ubuntu2 (Ubuntu)

Type 'help;' or '\h' for help. Type '\c' to clear the buffer.

mysql> select 0.0 / 0;
+---------+
| 0.0 / 0 |
+---------+
|    NULL |
+---------+
1 row in set (0.00 sec)

mysql> select NaN, NaN is null, NaN = NaN, NaN is false
    -> from ( select 0.0 / 0 as NaN) s;
+------+-------------+-----------+--------------+
| NaN  | NaN is null | NaN = NaN | NaN is false |
+------+-------------+-----------+--------------+
| NULL |           1 |      NULL |            0 |
+------+-------------+-----------+--------------+
1 row in set (0.00 sec)

なるほど。MySQLではNaNのかわりとしてNULLで実装されていますね。
これはSQLではNULL = NULLがtrueでもfalseでもなく、NULLだから…ってことでしょうか。


ttさんの指摘。


本当はNaNは比較さえできないものなので、比較を含めてなんらかの処理を行った段階で例外を起こす(Signaling NaN)か、そうでなければ(QuietNaNであれば)仕方ないのでNaN!=NaNとなるのがIEEE754的な標準です。これは数学ではなく工学規格でありますが。
となると、NaN同士の比較は特別扱いにする(か、そこまで考慮した比較の仕様にする)必要が出てくるってことですね。
どうもセンスがないので、理解に時間かかりそうですorz

新しいタブが進化していた

先ほど気づいたのですが、Chrome3系(dev版)で「新しいタブ」ページが変更されていました。
見た目こんな感じ

Chromeの3.0.193.0で確認。
ぱっと見たところの相違点は、

  • 「Tips and Suggestions」という謎の領域が
  • 「最近追加したブックマーク」の消失
  • 「Recent Activities(最近の活動…くらいに訳されるかな)」に、最近のダウンロード結果が
  • 「よく使うページ」のサムネの並びが変わった

あたりでしょうか


私がまず気になったのは、Tips and Suggestionsという謎の領域についてです。
いまのところ、トリガーになる動作を私がしていないのか、実装されていないのかは分からないのですが、とりあえず私の手元では動作した状況が見れていません。
そこで想像するしかなくなるわけですが、もしこれがChromeの使い方についてのTipsやら提案ならば、長く使っているユーザには不要な領域となっていくことは自明でしょうし、Googleっぽい情報エントロピーに配慮した画面配置から考えると、少し違和感があります。
よく使うページやらブックマークやらの活動から類推した、ページリコメンド機能あたりかなぁと想像すると納得いくわけですが、それだと言葉に違和感があるわけです。
ここは実際に動作した姿を待つとするしかないわけですが、だいぶ興味深い領域ですね。


次に気になったのは、「最近追加したブックマーク」の消失です。
よっぽど不評だったのでしょうか。
…私には、あの機能の問題点は、削除機能のUI部分にしか無さそうに思えたのですが。
ブックマークであっても時間的局所性はありそうなので、有用なモノだったはず…と思いつつ、そういえばあまり使ってなかったなぁと自分の行動を思い起こすことも。ん〜。どうしてでしょうね。


「Recent Activities」としてダウンロード結果が見えるのは嬉しいですね。
ダウンロード中の表示が嫌で消してしまうと、落としたファイルを見失いそうになってCtrl+Jで履歴みていたのですが、新しいタブにもあるとダウンロード履歴ページを気にしなくてよくなるので、探しやすくなりそうです。


「よく使うページ」のサムネイルの配置は、配置変更ではなく削除しやすい機能をつけた点でもよい感じです。
サムネイルにカーソルを合わせると、表示が下のように変わります。

この右上にある×ボタンで、よく使うページ一覧から該当ページを削除することができます。
またドラッグ&ドロップで位置を変更したり、PINを設定したり(どう動くか、うまく実験できませなんだ)もできるようです。


このサムネイルの表示方法は、リスト形式とサムネイル形式があり、右上のボタンで変更できるようになっています。


そして、これらをひっくるめて、表示・非表示の制御をする機能がついています。

右上の▼を押すと出てくるリストでもって、各領域を表示したり隠せたり、遊べるわけですね。


まだ正式版は出てないわけですが、「新しいタブ」をキラー機能と位置づけているのか、更なる進化を追及してきたChrome。これからも目が離せませんね。

JavaにおけるNaN比較

先日の続き。
Javaではどうなるか気になったので試してみました。

C:\Develop>copy con NaN.java
public class NaN {
  public static void main (String[] args) {
    System.out.println(0.0 / 0 == 0.0 / 0);
  }
}
^Z
        1 個のファイルをコピーしました。

C:\Develop>^Z

C:\Develop>\Develop\Language\jdk1.6.0_07\bin\javac.exe NaN.java

C:\Develop>\Develop\Language\jdk1.6.0_07\bin\java.exe NaN
false

普通にfalseになるようです。
実際はどんな動きをしているか、javapを使ってみてみました。

C:\Develop>\Develop\Language\jdk1.6.0_07\bin\javap.exe -verbose NaN
(中略)
public static void main(java.lang.String[]);
  Code:
   Stack=2, Locals=1, Args_size=1
   0:   getstatic       #2; //Field java/lang/System.out:Ljava/io/PrintStream;
   3:   iconst_0
   4:   invokevirtual   #3; //Method java/io/PrintStream.println:(Z)V
   7:   return
  LineNumberTable:
   line 3: 0
   line 4: 7

…あ、コンパイラが最適化してたorz


というわけで、やりなおし。

C:\Develop>del NaN.*

C:\Develop>copy con NaN.java
public class NaN {
  public static float getZero() {
    return 0.0f;
  }
  public static void main(String[] args) {
    System.out.println(getZero() / 0 == getZero() / 0);
  }
}
^Z
        1 個のファイルをコピーしました。

C:\Develop>\Develop\Language\jdk1.6.0_07\bin\javac.exe NaN.java

C:\Develop>\Develop\Language\jdk1.6.0_07\bin\javap.exe -verbose NaN
(中略)
public static void main(java.lang.String[]);
  Code:
   Stack=4, Locals=1, Args_size=1
   0:   getstatic       #2; //Field java/lang/System.out:Ljava/io/PrintStream;
   3:   invokestatic    #3; //Method getZero:()F
   6:   fconst_0
   7:   fdiv
   8:   invokestatic    #3; //Method getZero:()F
   11:  fconst_0
   12:  fdiv
   13:  fcmpl
   14:  ifne    21
   17:  iconst_1
   18:  goto    22
   21:  iconst_0
   22:  invokevirtual   #4; //Method java/io/PrintStream.println:(Z)V
   25:  return
(中略)

C:\Develop>\Develop\Language\jdk1.6.0_07\bin\java.exe NaN
false

今度は問題無さそうです。そして動作は、やはりfalseでした。
# しかし、実行時最適化されているかどうかは不明です
# このあたりのJavaの動きは、ある種クレイジーで面白いわけですが。


では、計算しない場合はどうなのでしょう。

C:\Develop>copy con StaticNaN.java
public class StaticNaN {
  public static float getNaN() {
    return Float.NaN;
  }
  public static void main(String[] args) {
    System.out.println(getNaN() == getNaN());
  }
}
^Z
        1 個のファイルをコピーしました。

C:\Develop>\Develop\Language\jdk1.6.0_07\bin\javac.exe StaticNaN.java

C:\Develop>\Develop\Language\jdk1.6.0_07\bin\javap.exe -verbose StaticNaN
(中略)
public static void main(java.lang.String[]);
  Code:
   Stack=3, Locals=1, Args_size=1
   0:   getstatic       #3; //Field java/lang/System.out:Ljava/io/PrintStream;
   3:   invokestatic    #4; //Method getNaN:()F
   6:   invokestatic    #4; //Method getNaN:()F
   9:   fcmpl
   10:  ifne    17
   13:  iconst_1
   14:  goto    18
   17:  iconst_0
   18:  invokevirtual   #5; //Method java/io/PrintStream.println:(Z)V
   21:  return
(中略)
C:\Develop>\Develop\Language\jdk1.6.0_07\bin\java.exe StaticNaN
false

やはりfalseです。

こちらもPythonと同じ結果が得られました。

実装上、たまたまそうなっているだけでしょうか。
Java言語仕様を見ると、


a numeric comparison operation involving one or two NaNs returns false and any != comparison involving NaN returns true, including x!=x when x is NaN
と、同一の変数であっても==での比較結果は常にfalseと明示的に書かれています。
世の中的には、どうやらx=NaNの時、x==xはfalseのようです。


しかし、それと数学とが一致しているかは、キット別の話なのかなぁと思いつつ、数学が弱いので判別つきません。
ん〜。数学の勉強、どっかでしなければ。

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となるようです。

情報処理試験 2009春 結果編

4月に受けたデータベーススペシャリスト試験の結果が出たのでチェックしてみました。
結果
午前I 得点 85.00点
午前II得点 80.00点
午後I 得点 75点
午後II得点 73点
全て60点以上で合格なので、無事合格できました。


しかし、私の感覚はまだ、DBの設計検査についていまだ感覚が鈍い状態です。
もっともっと多くの設計を見て、感覚を研ぎ澄ます訓練はしていかなければならないと思うわけで、これをゴールと思わずにがんばらねばです。

新しいタブページの情報を制御する

InfoQのChromeに関する記事を見て、とあるところに目が止まった。

Chromeの心地よくも注意したい機能

新しいタブページ: ユーザから最も多く寄せられた要望は「新しいタブ」ページからサムネイルを削除する機能でした。これからは「よくアクセスするページ」から恥ずかしいゴシップブログを隠すことが出来るようになります。
Chromeの新しいタブの活用は、素晴らしいハックだと思う。
しかしこれには注意が必要で、意図しない状況で意図しない相手に意図しないものを見せてしまう可能性がある。例えば婚約指輪だ。そんなものは、ちゃんと意表をついてプロポーズした時に渡さないとカッコが付かないだろう。
新しいタブには、婚約指輪を買おうと検討していたジュエリーショップが表示されてしまうかもしれない場所が4つある。
一つ目が「ブックマークバー」。「ブックマークバーを常に表示しない」設定にしていても、新しいタブには表示されてしまう。しかしブックマークは編集可能だから、自己責任でスグに見えない場所に収めることが可能だ。秘密にしたいものは奥深くするといいだろう。
二つ目が「よくアクセスするページ」。HTTPSに注意を払いつつサムネイルが表示され、われわれの行動を強くアシストしてくれる素晴らしい機能だ。今回削除できるようになったのは、ここのサムネイルだろう。
三つ目が「最近追加したブックマーク」。これは以前に、最近追加したブックマークから任意のものを除外する方法を書いた。応用すれば追加・編集も自在だろう。未だにここの操作は、標準のインターフェースではできない模様。今のところChrome3系でもできない。
四つ目が「最近閉じたタブ」。私の場合で恐縮だけど、うっかりと読むべきところがあるタブを閉じたり、閉じた後に「ああ、さっきのっ」って思う状況が発生する。大抵こういう時は、その閉じたタブで表示していたページは、Historyから攫ってくるのが面倒なモノだったりするわけだ。

早速試してみる

そういえば自宅には2系を入れているPCがなかったということを思い出し、普段はあまり使わない自宅のWindowsXPなマシンにChrome 2を入れてみた。
立ち上げてみると、確かに「サムネイルを削除」の表示がある。

そこで「よくアクセスするページ」にいくつかのページを表示させ、サムネイルを削除させてみた。



無事削除成功。
しかし、表示されているサムネイル数が3を切ると、一挙に全部ふっとぶらしい。
それはそれでどうなのだろう。
とりあえず、直近でごにょごにょと検討していたページは表示されなくなるというわけだ。
ちゃんとシークレットモードを使っていればいいけど、

ブックマークは実装が変わっている

1系からだったのか2系からなのかは分からないが、ブックマークの実装が変わっている。
これはChrome3系でも同じだ。
上に挙げた以前のエントリでは、SQLiteを駆使しなければならなかった部分が、テキストエディタでもいけるようになっている。
場所はXPの場合、

C:\Documents and Settings\quabbin\Local Settings\Application Data\Google\Chrome\User Data\Default\Bookmarks

Vistaの場合、

C:\Users\quabbin\AppData\Local\Google\Chrome\User Data\Default\Bookmarks

だだし双方とも、quabbin の部分は自分のユーザ名で置き換える必要がある。
中身を見ると、JSON形式になっている。

{
   "roots": {
      "bookmark_bar": {
         "children": [ {
            "date_added": "12889114615623003",
            "name": "\u8305\u539F\u5B9F\u91CC\u30AA\u30D5\u30A3\u30B7\u30E3\u30EB\u30B5\u30A4\u30C8-Minori Chihara Official Web Site-",
            "type": "url",
            "url": "http://chiharaminori.jp/"
         } ],
         "date_added": "0",
         "date_modified": "12889114615623003",
         "name": "\u30D6\u30C3\u30AF\u30DE\u30FC\u30AF \u30D0\u30FC",
         "type": "folder"
      },
      "other": {
         "children": [  ],
         "date_added": "0",
         "date_modified": "0",
         "name": "\u305D\u306E\u4ED6\u306E\u30D6\u30C3\u30AF\u30DE\u30FC\u30AF",
         "type": "folder"
      }
   },
   "version": 1
}

ここの date_addedを小さい値にすれば、ブックマークは変わる

が、こうなる

…つまり、ブックマーク数が少ないときには効果がないって事で…ちゃんと多いときには利きます。
お見せできないけどorz

chromeは細かく制御できるようになってきた

こうして、少し気をつけさえすれば、彼女に内緒で婚約指輪の検討がバレ、折角のプロポーズを水泡に帰す可能性を少なくすることが出来ます。
少しづつではありますが、柔軟に設定や編集が出来るようになってきていているようです。

JNDIの活用 〜Tomcat 6.0.18の脆弱性に思う事〜

Tomcat6.0.18以下に脆弱性があると発表があったのですが、内容を見て少し考えこんでしまいました。

IPAのレポートを要約してみる

IPAによるとApache Tomcat 6系の6.0.18以下に情報漏えいの脆弱性が発見された模様。同日に公開された脆弱性も共に対策済みの6.0.20が出ているため、該当バージョンを使用している方は6.0.20にアップデートが必要だそうだ。
同レポートによると、4.1.0〜4.1.39、5.5.0〜5.5.27にも影響があり、3.0系・4.0系・5.0系も影響がある可能性がある。
また同レポートは、対応版が出ていない4.1.xと5.5.xを利用している時は、snvからコンパイルするようにアドバイスしている。
また脆弱性の内容は

  • 特定メソッドを利用した実装の時、WEB-INF ディレクトリ配下の情報が漏えい
  • AJPコネクタでlbワーカを利用している場合、約1分間利用不可能になる

ということで、WEBアプリケーションの実装次第では情報漏洩やDoSの危険性が指摘されている。

JNDIを使えば楽に…ならないんだよね…

WEB-INF下の情報が漏洩すると困るような実装は、JNDIをしっかり使えば退避可能でしょう。
その際に大きく問題になると思われるのは二点。

  • 設定の標準的な管理方法がない
  • 例えばTomcatの場合はJava Mailに対するリソース設定が標準ではない(作ることは可能)など、標準的に使える設定種類が不足している。

詳しく見てみます。

設定の管理方法について

前者…は私の勘違いかもしれないのですが…。
例えばDataSource設定の場合、IPとIDとパスワードといったリソース値を管理する方法が、APサーバによってバラバラで統一感がありません。
それ自体はセキュリティなどに対する標準的なポリシーがAPサーバ毎に違うため致し方ないのですが、値がヤンゴトナキ事情により変更となる場合、大抵APサーバのサービス再起動が必要となるでしょう。
また、GUIなどから値をいじれる場合、その永続化がされる場合とされない場合があったりします。
まずAPサーバがオンラインのままでも設定値が永続化でき、サービス全体を再起動しなくても反映でき、更に設定値の棚卸しに対応できるようなインターフェースを備えた仕組みが、共通的にあるとJNDIが使いやすいのだけど…という、タダのグチです。

リソース設定の標準的な形がない

せめてSMTPの設定くらいはほしいし、欲を言えばSOAPで使用するIDやパスワードなども管理したいわけだけど、出来るAPサーバと出来ないAPサーバがある。
特に商用APサーバを利用する場合、結構開発環境はTomcatだのGlass FishだのJBossだのといった具合で済ませている場合が多く、これらのAPサーバ達(サーブレットコンテナと呼んだほうがイイのかもしれない)が対応してくれないことには、アプリケーション実装者達は使いたがらないだろう。
上流から命令が下れば、何とかするのだろうけど。
# ちなみに私の場合は、自分でやりたかったからやっただけです

JNDIが使いやすければ、もっとよくなるのに

と、今のところ二点の問題があって、JNDIはなかなか設定ファイル代わりとして使用されない状況にあるのではないかと推測しております。
そのあたりを整理が出来たら面白そうなのですが。

それはそうと、Tomcatを利用したアプリケーション…例えばJBossなどを使っている場合は、対応できるまでに時間がかかると考えるべきなのでしょうか。