ruby-changes:24353
From: akr <ko1@a...>
Date: Mon, 16 Jul 2012 18:09:10 +0900 (JST)
Subject: [ruby-changes:24353] akr:r36404 (trunk): * bignum.c (rb_big_float_cmp): compare an integer and float precisely.
akr 2012-07-16 18:08:58 +0900 (Mon, 16 Jul 2012) New Revision: 36404 http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=36404 Log: * bignum.c (rb_big_float_cmp): compare an integer and float precisely. [ruby-core:31376] [Bug #3589] reported by Tomasz Wegrzanowski. Modified files: trunk/ChangeLog trunk/bignum.c trunk/test/ruby/test_float.rb Index: ChangeLog =================================================================== --- ChangeLog (revision 36403) +++ ChangeLog (revision 36404) @@ -1,3 +1,8 @@ +Mon Jul 16 17:57:54 2012 Tanaka Akira <akr@f...> + + * bignum.c (rb_big_float_cmp): compare an integer and float precisely. + [ruby-core:31376] [Bug #3589] reported by Tomasz Wegrzanowski. + Mon Jul 16 17:29:45 2012 Tanaka Akira <akr@f...> * bignum.c (rb_big_float_cmp): support fixnum for argument x. Index: bignum.c =================================================================== --- bignum.c (revision 36403) +++ bignum.c (revision 36404) @@ -1435,6 +1435,8 @@ rb_big_float_cmp(VALUE x, VALUE y) { double a = RFLOAT_VALUE(y); + double yi, yf; + VALUE rel; if (isnan(a)) return Qnil; @@ -1442,15 +1444,41 @@ if (a > 0.0) return INT2FIX(-1); else return INT2FIX(1); } + yf = modf(a, &yi); if (FIXNUM_P(x)) { +#if SIZEOF_LONG * CHAR_BIT < DBL_MANT_DIG /* assume FLT_RADIX == 2 */ double xd = (double)FIX2LONG(x); if (xd < a) return INT2FIX(-1); if (xd > a) return INT2FIX(1); return INT2FIX(0); +#else + long xl, yl; + if (yi < LONG_MIN) + return INT2FIX(1); + if (LONG_MAX < yi) + return INT2FIX(-1); + xl = FIX2LONG(x); + yl = (long)yi; + if (xl < yl) + return INT2FIX(-1); + if (xl > yl) + return INT2FIX(1); + if (yf < 0.0) + return INT2FIX(1); + if (0.0 < yf) + return INT2FIX(-1); + return INT2FIX(0); +#endif } - return rb_dbl_cmp(rb_big2dbl(x), a); + y = rb_dbl2big(yi); + rel = rb_big_cmp(x, y); + if (yf == 0.0 || rel != INT2FIX(0)) + return rel; + if (yf < 0.0) + return INT2FIX(1); + return INT2FIX(-1); } /* Index: test/ruby/test_float.rb =================================================================== --- test/ruby/test_float.rb (revision 36403) +++ test/ruby/test_float.rb (revision 36404) @@ -57,6 +57,55 @@ assert_equal(a == b, b == a) end + def test_cmp_int + 100.times {|i| + int0 = 1 << i + [int0, -int0].each {|int| + flt = int.to_f + bigger = int + 1 + smaller = int - 1 + assert_operator(flt, :==, int) + assert_operator(flt, :>, smaller) + assert_operator(flt, :>=, smaller) + assert_operator(flt, :<, bigger) + assert_operator(flt, :<=, bigger) + assert_equal(0, flt <=> int) + assert_equal(-1, flt <=> bigger) + assert_equal(1, flt <=> smaller) + assert_operator(int, :==, flt) + assert_operator(bigger, :>, flt) + assert_operator(bigger, :>=, flt) + assert_operator(smaller, :<, flt) + assert_operator(smaller, :<=, flt) + assert_equal(0, int <=> flt) + assert_equal(-1, smaller <=> flt) + assert_equal(1, bigger <=> flt) + [ + [int, flt + 0.5, bigger], + [smaller, flt - 0.5, int] + ].each {|smaller2, flt2, bigger2| + next if flt2 == flt2.round + assert_operator(flt2, :!=, smaller2) + assert_operator(flt2, :!=, bigger2) + assert_operator(flt2, :>, smaller2) + assert_operator(flt2, :>=, smaller2) + assert_operator(flt2, :<, bigger2) + assert_operator(flt2, :<=, bigger2) + assert_equal(-1, flt2 <=> bigger2) + assert_equal(1, flt2 <=> smaller2) + assert_operator(smaller2, :!=, flt2) + assert_operator(bigger2, :!=, flt2) + assert_operator(bigger2, :>, flt2) + assert_operator(bigger2, :>=, flt2) + assert_operator(smaller2, :<, flt2) + assert_operator(smaller2, :<=, flt2) + assert_equal(-1, smaller2 <=> flt2) + assert_equal(1, bigger2 <=> flt2) + } + } + } + end + def test_strtod a = Float("0") assert(a.abs < Float::EPSILON) -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/