From: Ikuo KOBORI Date: 2010-08-15T21:09:52+09:00 Subject: [ruby-dev:42039] [Bug #3696] Fwd: [ruby-list:47272] case when 整数と実数の比較 (ruby 1.9) チケット #3696 が更新されました。 (by Ikuo KOBORI) 小堀といいます。 以下のようにして修正できましたので、報告します。 whenにあらわれるのがすべて定数のとき、整数だろうと浮動小数点数であろうとハッシュテーブルに登録しています。 すると小数と整数の組み合わせでは、比較しても一致しなくなってしまいます(1 と 1.0 の場合など)。 なので小数を整数に丸めても精度が落ちないときは、変換してからテーブルに登録するようにしました。case に小数があらわれたときも同様です。 その他の場合にはテーブルを作ったとしても、入る物と型が一致していないと値は同じになることはないので、それぞれ特別扱いにしました。こうすることで case に独自のオブジェクトが渡されたときに対応できます。例えばつぎのような場合です。 class C def ==(other) true end end obj = C.new case obj when 1 p "OK" else p "NG" end このときにも "OK" と出力されるようになります。 diff --git a/compile.c b/compile.c index 2fd804c..f5f3508 100644 --- a/compile.c +++ b/compile.c @@ -2302,14 +2302,22 @@ case_when_optimizable_literal(NODE * node) { switch (nd_type(node)) { case NODE_LIT: { - VALUE v = node->nd_lit; - if (SYMBOL_P(v) || rb_obj_is_kind_of(v, rb_cNumeric)) { - return v; - } - break; + VALUE v = node->nd_lit; + if ((SYMBOL_P(v) || rb_obj_is_kind_of(v, rb_cInteger))) { + return v; + } + if (rb_obj_is_kind_of(v, rb_cFloat)) { + if (RFLOAT_VALUE(v) == (long long)RFLOAT_VALUE(v)) { + return rb_funcall(v, rb_intern("to_i") , 0); + } + else { + return v; + } + } + break; } case NODE_STR: - return node->nd_lit; + return node->nd_lit; } return Qfalse; } diff --git a/insns.def b/insns.def index fcd97ae..bcf5aa8 100644 --- a/insns.def +++ b/insns.def @@ -1260,28 +1260,40 @@ opt_case_dispatch (..., VALUE key) () // inc += -1; { - if (BASIC_OP_UNREDEFINED_P(BOP_EQQ)) { - VALUE val; - if (st_lookup(RHASH_TBL(hash), key, &val)) { - JUMP(FIX2INT(val)); - } - else { - JUMP(else_offset); - } - } - else { - struct opt_case_dispatch_i_arg arg; + struct opt_case_dispatch_i_arg arg; - arg.obj = key; - arg.label = -1; - st_foreach(RHASH_TBL(hash), opt_case_dispatch_i, (st_data_t)&arg); - - if (arg.label != -1) { - JUMP(arg.label); - } - else { - JUMP(else_offset); - } + switch(TYPE(key)) + { + case T_FLOAT: + if (RFLOAT_VALUE(key) == (long long)RFLOAT_VALUE(key)) { + key = rb_funcall(key, rb_intern("to_i") , 0); + } + case T_SYMBOL: /* fall through */ + case T_FIXNUM: + case T_BIGNUM: + case T_STRING: + if (BASIC_OP_UNREDEFINED_P(BOP_EQQ)) { + VALUE val; + if (st_lookup(RHASH_TBL(hash), key, &val)) { + JUMP(FIX2INT(val)); + } + else { + JUMP(else_offset); + } + break; + } + default: /* fall through (else) */ + arg.obj = key; + arg.label = -1; + st_foreach(RHASH_TBL(hash), opt_case_dispatch_i, (st_data_t)&arg); + + if (arg.label != -1) { + JUMP(arg.label); + } + else { + JUMP(else_offset); + } + break; } } ---------------------------------------- http://redmine.ruby-lang.org/issues/show/3696 ---------------------------------------- http://redmine.ruby-lang.org