えぐち@エスアンドイー です。

>>> In message [ruby-math:00105] Re: NaN again
    On Mon, 24 Jan 2000 03:28:40 +0900 (JST), gotoken / math.sci.hokudai.ac.jp (GOTO Kentaro) said:

ご> ごとけんです
ご> 
ご> In message "[ruby-math:00103] Re: NaN again"
ご>     on 00/01/22, EGUCHI Osamu <eguchi / shizuokanet.ne.jp> writes:
ご> 
ご> ><=> に関する奇妙な挙動として強いて言えば、 Inf <=> Inf があります。 
ご> >Inf - Inf は、NaN ですが Inf == Inf は、真 Inf <=> Inf は 0
ご> >#finite <=> NaN とは様子が違いますが、
ご> >#比較と減算は完全には対応しない実例として、あげられます。
ご> 
ご> 無限大はNaNと違って一意な存在だからなんでしょうね。こーゆー
ご> 風変わりな挙動はRuby上でも楽しみたいです ^^;;

Inf - Inf が 0 でなく、NaN なのは必然と言えば必然ですが、
数学的には『無効』ですので例外の対象にあるわけで、
値を返す必要に迫られると、NaN(非数: そんな計算に値は求まらない)
となるわけで、IEEE754 を作った人達の苦労が現れている所ですね。

ご> >こう言うと、 finite <=> NaN → NaN 論と整合しないようですが、、、
ご> >IEEE754 の例外のディフォルトの挙動は「なにもしない」なので、
ご> >Ruby では何もしないで NaN を返す様ことが妥当だと考えたわけです。
ご> 
ご> なにもしないがNaNというのはちょっと気になります。まぁNaNを含
ご> む演算は一方のNaNをそのまま(ビットイメージを保ったまま)返す
ご> という例外とは別の原則があるのでそれはそれでよいやも知れませ
ご> ん。

この『ビットイメージを保ったまま』ってのが重要だと思います。
というのは、NaN のビットイメージに隠れた情報を埋め込む事が
NaN を産んだ原因を同定する手がかりになるかも知れませんから。

ご> >「全順序でないものに <=> を実装したい場合」の
ご> >『全順序でないもの』って Set (集合) の様な物を想定してますか?
ご> 
ご> そこまで激しくなくても、NaN的な存在を含むほぼ全順序な構造、
ご> たとえば文字列の配列の配列のクラスAoAの特例として空の配列が
ご> あるような場合を考えていました。たとえば
ご> 
ご> AoA[["abc"], ["abc", "efc"]] <=> AoA[]
ご> 
ご> とか。まぁ最小元としてもいいけど、そうしたくない場合もあるで
ご> しょう。

最小元としたくない場合がどういうケースか、直観的に思い付かないですが、
空の配列とか空集合の重みは、悩まされるところですね。
#類似概念だと、 lisp の () → nil とか、、、

ご> >Set a と Set b に置いて、
ご> >  a が b の部分集合 ⇒ a <=> b → -1
ご> >  b が a の部分集合 ⇒ a <=> b →  1
ご> >  a が b の部分集合でなく、b が a の部分集合でない ⇒ a <=> b → NaN 
ご> >でしょうか?(集合の比較に <=> は無理あり?)
ご> 
ご> 半順序に <=> は良くないでしょうね。

「良くない」んですが、未定義とするか、例外とするか悩ましいですね。

ご> >要するに finite <=> NaN の時(及び NaN <=> finete の時)
ご> >
ご> >   Numeric でない有意な値を返し、例外は発生させない
ご> >
ご> >のであれば、「Numeric でない有意な値」は何でもいいです。 ^^;;;
ご> 
ご> うう、僕は kind_of? Numeric であるところの NaN を返すという
ご> のがイカすと思っていたのでした ^^;;
ご> 
ご> 意味の無い値という意味では nil に勝るモノはなさそうです。

NaN.type → Float と言うのが引っかかる所ですが、
NaN を返すのは、結構イケテルと思います。
#その NaN が、比較の(この場合は右辺の)NaN である事も重要です。

「数のフォーマットに非数(NaN)がある」という事に
違和感を持つ場合もありますが、nil や false よりも
ユーザプログラムに裁量をあたえる可能性があって好ましく思えます。
#NaN に意味がある、とすると意味を取り出す procedure が必要そうだけど。

ご> >mzero() の -0.0 の作り方が凝ってますね。(^^)
ご> >
ご> >ところで、
ご> >
ご> >	double p = +0.0;
ご> >	double n = -0.0;
ご> >	return memcmp(&p, &n, sizeof p) != 0;
ご> >
ご> >程度だとまずいでしょうか?
ご> 
ご> -0.0 がコンパイラにどう解釈されるかにちょっと疑問を持ったの
ご> で確実な -1.0 を使ってみました。

すみません、これは

	double p = +0.0;
→	double n = -p;
	return memcmp(&p, &n, sizeof p) != 0;

の間違いです。

やはり、コンパイラが何を考えるか判らないので、
(ieee754の単精度か倍精度であるとするなら)ビットの検査や
操作を行うのが、より堅実ですね。

ご> >ご> # isnan() もこんな感じで実装しちゃった方が確実かも。
ご> >
ご> >NaN を変数に入れて、引数とビット毎の比較と言う事ですね。
ご> 
ご> >あ、それとも  NaN != NaN ⇒ true がうまく行っていたと思っていたら、
ご> >じつは右辺と左辺は違うビットパタンの NaN だったってケースの問題?
ご> 
ご> そうそう、そっちの問題です。ぼくは Intel しか知らないのです
ご> が、そういういう変わったことをするやつもあるかと思いまして。

NaN については、configure で isnan が厳密に行われているかの
テストを行い、ビットパターンを網羅的に検証する必要があるかも知れません。
#80x87 的には OK でも、コンパイラが不適当な最適化を行う場合もあるので。

ご> しかしエンディアンの処理は結構頭痛いので isnan() を書くとし
ご> たら、pack.c と同様な技法を使う必要がありそうです。しかし、
ご> Float#to_i や to_s でつかってるからあんまり遅いのもまずいなぁ。
ご> やっぱりこれはボツか。地道に処理系の抜け道を探しましょう :-<

基本的には、処理系の提供する isnan が十分妥当な挙動を
します事を確認してそれを使うのが良いと思います。
#何が、妥当な挙動かは議論の余地があるけれども。

もし、妥当でない挙動を示すのであれば、
missing/isnan.c に(VC の時のように)頑張ってもらうしかないですね。
#コンパイラのお節介な最適化との戦いになるかも知れません ;-p

	えぐち