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

>>> On Fri, 5 Feb 1999 00:32:15 +0900, えぐち said:
...
えぐち> Ruby の Float のメソッド <=> も NaN 対応が必要でしょう。
えぐち> 具体的には、NaN との大小判別例外を raise するのが良いと思います。
えぐち> 
えぐち> 	えぐち
えぐち> 
えぐち> #もっか numberic.c と格闘中につき、今回はパッチなし ^^)l

 Subject: [ruby-list:7029] Re: infinity
 From: shugo / po.aianet.ne.jp (Shugo Maeda)
 To: ruby-list / netlab.co.jp (ruby mailing list)
 Date: Tue, 10 Mar 1998 01:59:40 +0900

を参考に、INFINITY と NaN の扱いのパッチを作りました。

 * Float::INFINITY の追加
 * Float::NaN の追加
 * NaN との大小判別例外 NaNCompareError クラスの追加
 * FLoat#infinite? の追加(Inf:正数, -Inf:負数、otherwise false)
 * FLoat#nan? の追加(また x == Float::NaN は常に偽)
 * FLoat#finte? の追加(Inf, -Inf or NaN:false, otherwise true)
 * FreeBSD 環境での浮動小数点関連のトラップの抑止(main.c)

 実行例
    triton2.eguchi:83% ./ruby -e 'p ((0.0/0.0))'
    NaN
    triton2.eguchi:84% ./ruby -e 'p ((1.0/0.0))'
    INFINITY
    triton2.eguchi:85% ./ruby -e 'p ((1.0/0.0).to_s)'
    "INFINITY"
    triton2.eguchi:86% ./ruby -e 'p ((-1.0/0.0).to_s)'
    "-INFINITY"
    triton2.eguchi:87% ./ruby -e 'p ((0.0/0.0).to_s)'
    "NaN"
    triton2.eguchi:88% ./ruby -e 'p ((1.0/0.0).nan?)'
    false
    triton2.eguchi:89% ./ruby -e 'p ((0.0/0.0).nan?)'
    true
    triton2.eguchi:90% ./ruby -e 'p ((1.0/0.0).nan?)'
    false
    triton2.eguchi:91% ./ruby -e 'p ((1.0/0.0).finite?)'
    false
    triton2.eguchi:92% ./ruby -e 'p ((1.0/1.0).finite?)'
    true
    triton2.eguchi:93% ./ruby -e 'p ((0.0/0.0).finite?)'
    false
    triton2.eguchi:94% ./ruby -e 'p ((-1.0/0.0).finite?)'
    false

作って見たものの、より多くの人の意見を伺いたいと思います。

特に NaN との大小関係(<=>)の問題は
例外を上げるのが正しいのか迷いがあります。

また、INFINITY と NaN で print するので、.to_f も
合わせるべきでしょうか?

採用頂けるなら、Marshal も影響がある様に思えます。
(Pack は多分影響なし)

	えぐち

diff -rud ../ruby-1.3.1-990205/main.c ./main.c --- ../ruby-1.3.1-990205/main.c Tue Jan 26 19:08:13 1999 +++ ./main.c Sat Feb 6 01:40:40 1999 @@ -21,6 +21,9 @@ #if defined(__MACOS__) && defined(__MWERKS__) #include <console.h> #endif +#ifdef __FreeBSD__ +#include <floatingpoint.h> +#endif int main(argc, argv, envp) @@ -32,6 +35,10 @@ #endif #if defined(__MACOS__) && defined(__MWERKS__) argc = ccommand(&argv); +#endif +#ifdef __FreeBSD__ + /* allow divide by zero -- Inf */ + fpsetmask(fpgetmask() & ~(FP_X_DZ|FP_X_INV)); #endif ruby_init(); diff -rud ../ruby-1.3.1-990205/numeric.c ./numeric.c --- ../ruby-1.3.1-990205/numeric.c Fri Feb 5 19:27:31 1999 +++ ./numeric.c Sat Feb 6 01:38:18 1999 @@ -23,6 +23,7 @@ VALUE rb_cFixnum; VALUE rb_eZeroDiv; +VALUE rb_eNaNComp; ID rb_frame_last_func(); VALUE rb_float_new(); @@ -34,6 +35,12 @@ rb_raise(rb_eZeroDiv, "divided by 0"); } +void +rb_num_nancomp() +{ + rb_raise(rb_eNaNComp, "compare to NaN"); +} + static VALUE num_coerce(x, y) VALUE x, y; @@ -185,13 +192,18 @@ { char buf[24]; char *s; + double d = RFLOAT(flt)->value; - sprintf(buf, "%-.10g", RFLOAT(flt)->value); + if (isinf(d)) { + return rb_str_new2(d > 0 ? "INFINITY" : "-INFINITY"); + } + if (isnan(d)) { + return rb_str_new2("NaN"); + } + snprintf(buf, 24, "%-.10g", RFLOAT(flt)->value); if (s = strchr(buf, ' ')) *s = '\0'; s = buf; if (s[0] == '-') s++; - if (strchr(s, '.') == 0 && - strcmp(s, "Inf") != 0 && - strcmp(s, "NaN") != 0) { + if (strchr(buf, '.') == 0) { int len = strlen(buf); char *ind = strchr(buf, 'e'); @@ -273,16 +285,11 @@ flo_div(x, y) VALUE x, y; { - long f_y; - double d; - switch (TYPE(y)) { case T_FIXNUM: - f_y = FIX2LONG(y); - return rb_float_new(RFLOAT(x)->value / (double)f_y); + return rb_float_new(RFLOAT(x)->value / (double)FIX2INT(y)); case T_BIGNUM: - d = rb_big2dbl(y); - return rb_float_new(RFLOAT(x)->value / d); + return rb_float_new(RFLOAT(x)->value / rb_big2dbl(y)); case T_FLOAT: return rb_float_new(RFLOAT(x)->value / RFLOAT(y)->value); default: @@ -433,6 +440,9 @@ default: return rb_num_coerce_bin(x, y); } + if (isnan(a) || isnan(b)) { + rb_num_nancomp(); + } if (a == b) return INT2FIX(0); if (a > b) return INT2FIX(1); return INT2FIX(-1); @@ -637,6 +647,36 @@ } static VALUE +flo_infinite_p(flt) + VALUE flt; +{ + if (isinf(RFLOAT(flt)->value)) + return RFLOAT(flt)->value > 0.0 ? +1 : -1; + else + return Qfalse; +} + +static VALUE +flo_nan_p(flt) + VALUE flt; +{ + if (isnan(RFLOAT(flt)->value)) + return Qtrue; + else + return Qfalse; +} + +static VALUE +flo_finite_p(flt) + VALUE flt; +{ + if (finite(RFLOAT(flt)->value)) + return Qtrue; + else + return Qfalse; +} + +static VALUE to_integer(val) VALUE val; { @@ -1384,10 +1424,13 @@ void Init_Numeric() { + static double infinity, nan, zero = 0.0; + coerce = rb_intern("coerce"); to_i = rb_intern("to_i"); rb_eZeroDiv = rb_define_class("ZeroDivisionError", rb_eStandardError); + rb_eNaNComp = rb_define_class("NaNCompareError", rb_eStandardError); rb_cNumeric = rb_define_class("Numeric", rb_cObject); rb_include_module(rb_cNumeric, rb_mComparable); @@ -1468,6 +1511,10 @@ rb_define_method(rb_cFixnum, "zero?", fix_zero_p, 0); rb_cFloat = rb_define_class("Float", rb_cNumeric); + infinity = 1.0 / 0.0; + rb_define_const(rb_cFloat, "INFINITY", rb_float_new(infinity)); + nan = 0.0 / 0.0; + rb_define_const(rb_cFloat, "NaN", rb_float_new(nan)); rb_undef_method(CLASS_OF(rb_cFloat), "new"); @@ -1496,6 +1543,9 @@ rb_define_method(rb_cFloat, "to_f", flo_to_f, 0); rb_define_method(rb_cFloat, "abs", flo_abs, 0); rb_define_method(rb_cFloat, "zero?", flo_zero_p, 0); + rb_define_method(rb_cFloat, "infinite?", flo_infinite_p, 0); + rb_define_method(rb_cFloat, "nan?", flo_nan_p, 0); + rb_define_method(rb_cFloat, "finite?", flo_finite_p, 0); rb_define_method(rb_cFloat, "floor", flo_floor, 0); rb_define_method(rb_cFloat, "ceil", flo_ceil, 0);