Project

General

Profile

Actions

Bug #2661

closed

Bignum <= BigDecimal("NaN") raises an Exception

Added by mame (Yusuke Endoh) over 15 years ago. Updated about 14 years ago.

Status:
Closed
Assignee:
-
Target version:
-
ruby -v:
ruby 1.9.2dev (2010-01-27 trunk 26434) [i686-linux]
Backport:
[ruby-dev:40167]

Description

=begin
遠藤です。

Bignum と BigDecimal("NaN") を <= などで比較すると例外があがります。

$ ./ruby -rbigdecimal -ve 'p 2**100 <= BigDecimal("NaN")'
ruby 1.9.2dev (2010-01-27 trunk 26434) [i686-linux]
-e:1:in <=': comparison of Bignum with BigDecimal failed (ArgumentError) from -e:1:in '

ざっと確認した限りで、1.8.6-p388 、1.8.7-p249 、1.9.1-p378 、trunk のいずれ
でも発生します。

Comparable#<= が動き、その中で coerce と <=> が呼ばれますが、NaN との <=> は
nil が返るため、Comparable#<= が音を上げます。

Fixnum#<= の場合は、coerce を呼んだ後、coerce の結果に対して <= を呼ぶので
例外にはなりません。

Fixnum と同様に Bignum#<=, <, >= > を実装しないと対処できないような気が
します。

例によって、rubyspec がこれで落ちています。

BigDecimal#<= properly handles NaN values ERROR
ArgumentError: comparison of Bignum with BigDecimal failed
/home/mame/work/ruby/spec/rubyspec/library/bigdecimal/lte_spec.rb:77:in <=' /home/mame/work/ruby/spec/rubyspec/library/bigdecimal/lte_spec.rb:77:in block (4 levels) in <top (required)>'
/home/mame/work/ruby/spec/rubyspec/library/bigdecimal/lte_spec.rb:74:in each' /home/mame/work/ruby/spec/rubyspec/library/bigdecimal/lte_spec.rb:74:in block (3 levels) in <top (required)>'
/home/mame/work/ruby/spec/rubyspec/library/bigdecimal/lte_spec.rb:4:in `<top (required)>'

--
Yusuke Endoh
=end

Actions #1

Updated by mame (Yusuke Endoh) over 15 years ago

=begin
遠藤です。

2010年1月27日1:31 Yusuke Endoh :

Fixnum と同様に Bignum#<=, <, >= > を実装しないと対処できないような気が
します。

実装してみました。反対がなければコミットしたいと思います。

diff --git a/bignum.c b/bignum.c
index 0b464f2..d3b1d01 100644
--- a/bignum.c
+++ b/bignum.c
@@ -1329,6 +1329,113 @@ rb_big_cmp(VALUE x, VALUE y)
(RBIGNUM_SIGN(x) ? INT2FIX(-1) : INT2FIX(1));
}

+static VALUE
+big_op(VALUE x, VALUE y, int op)
+{

  • long xlen = RBIGNUM_LEN(x);
  • BDIGIT *xds, *yds;
  • VALUE rel;
  • int n;
  • switch (TYPE(y)) {
  •  case T_FIXNUM:
    
  •  case T_BIGNUM:
    
  • rel = rb_big_cmp(x, y);
  • break;
  •  case T_FLOAT:
    
  • {
  •  double a = RFLOAT_VALUE(y);
    
  •  if (isinf(a)) {
    
  •  if (a > 0.0) return INT2FIX(-1);
    
  •  else return INT2FIX(1);
    
  •  }
    
  •  rel = rb_dbl_cmp(rb_big2dbl(x), a);
    
  •  break;
    
  • }
  •  default:
    
  • {
  •  ID id = 0;
    
  •  switch (op) {
    
  •  case 0: id = '>'; break;
    
  •  case 1: id = rb_intern(">="); break;
    
  •  case 2: id = '<'; break;
    
  •  case 3: id = rb_intern("<="); break;
    
  •  }
    
  •  return rb_num_coerce_relop(x, y, id);
    
  • }
  • }
  • if (NIL_P(rel)) return Qfalse;
  • n = FIX2INT(rel);
  • switch (op) {
  • case 0: return n > 0 ? Qtrue : Qfalse;
  • case 1: return n >= 0 ? Qtrue : Qfalse;
  • case 2: return n < 0 ? Qtrue : Qfalse;
  • case 3: return n <= 0 ? Qtrue : Qfalse;
  • }
  • return Qundef;
    +}

+/*

    • call-seq:
    • big > real -> true or false
    • Returns true if the value of big is
    • greater than that of real.
  • */

+static VALUE
+big_gt(VALUE x, VALUE y)
+{

  • return big_op(x, y, 0);
    +}

+/*

    • call-seq:
    • big >= real -> true or false
    • Returns true if the value of big is
    • greater than or equal to that of real.
  • */

+static VALUE
+big_ge(VALUE x, VALUE y)
+{

  • return big_op(x, y, 1);
    +}

+/*

    • call-seq:
    • big < real -> true or false
    • Returns true if the value of big is
    • less than that of real.
  • */

+static VALUE
+big_lt(VALUE x, VALUE y)
+{

  • return big_op(x, y, 2);
    +}

+/*

    • call-seq:
    • big <= real -> true or false
    • Returns true if the value of big is
    • less than or equal to that of real.
  • */

+static VALUE
+big_le(VALUE x, VALUE y)
+{

  • return big_op(x, y, 3);
    +}

/*

  • call-seq:
  • big == obj  => true or false
    

@@ -3286,6 +3393,10 @@ Init_Bignum(void)

  rb_define_method(rb_cBignum, "<=>", rb_big_cmp, 1);
  rb_define_method(rb_cBignum, "==", rb_big_eq, 1);
  • rb_define_method(rb_cBignum, ">", big_gt, 1);
  • rb_define_method(rb_cBignum, ">=", big_ge, 1);
  • rb_define_method(rb_cBignum, "<", big_lt, 1);
  • rb_define_method(rb_cBignum, "<=", big_le, 1);
    rb_define_method(rb_cBignum, "===", rb_big_eq, 1);
    rb_define_method(rb_cBignum, "eql?", rb_big_eql, 1);
    rb_define_method(rb_cBignum, "hash", rb_big_hash, 0);

--
Yusuke ENDOH
=end

Actions #2

Updated by mame (Yusuke Endoh) over 15 years ago

  • Status changed from Open to Closed
  • % Done changed from 0 to 100

=begin
This issue was solved with changeset r26574.
Yusuke, thank you for reporting this issue.
Your contribution to Ruby is greatly appreciated.
May Ruby be with you.

=end

Actions

Also available in: Atom PDF

Like0
Like0Like0