[前][次][番号順一覧][スレッド一覧]

ruby-changes:46798

From: nobu <ko1@a...>
Date: Sat, 27 May 2017 10:26:36 +0900 (JST)
Subject: [ruby-changes:46798] nobu:r58913 (trunk): numeric.c: fix for small number

nobu	2017-05-27 10:26:31 +0900 (Sat, 27 May 2017)

  New Revision: 58913

  https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=58913

  Log:
    numeric.c: fix for small number
    
    * numeric.c (flo_floor, flo_ceil): should not return zero for small
      number.  [ruby-core:81394] [Bug #13599]

  Modified files:
    trunk/numeric.c
    trunk/test/ruby/test_float.rb
Index: numeric.c
===================================================================
--- numeric.c	(revision 58912)
+++ numeric.c	(revision 58913)
@@ -168,7 +168,8 @@ static int int_round_zero_p(VALUE num, i https://github.com/ruby/ruby/blob/trunk/numeric.c#L168
 VALUE rb_int_floor(VALUE num, int ndigits);
 VALUE rb_int_ceil(VALUE num, int ndigits);
 static VALUE flo_to_i(VALUE num);
-static int float_invariant_round(double number, int ndigits, VALUE *num);
+static int float_round_overflow(int ndigits, int binexp);
+static int float_round_underflow(int ndigits, int binexp);
 
 static ID id_coerce, id_div, id_divmod;
 #define id_to_i idTo_i
@@ -1933,28 +1934,30 @@ static VALUE https://github.com/ruby/ruby/blob/trunk/numeric.c#L1934
 flo_floor(int argc, VALUE *argv, VALUE num)
 {
     double number, f;
-    long val;
     int ndigits = 0;
 
     if (rb_check_arity(argc, 0, 1)) {
 	ndigits = NUM2INT(argv[0]);
     }
-    if (ndigits < 0) {
-	return rb_int_floor(flo_to_i(num), ndigits);
-    }
     number = RFLOAT_VALUE(num);
+    if (number == 0.0) {
+	return ndigits > 0 ? DBL2NUM(number) : INT2FIX(0);
+    }
     if (ndigits > 0) {
-	if (float_invariant_round(number, ndigits, &num)) return num;
+	int binexp;
+	frexp(number, &binexp);
+	if (float_round_overflow(ndigits, binexp)) return num;
+	if (number > 0.0 && float_round_underflow(ndigits, binexp))
+	    return DBL2NUM(0.0);
 	f = pow(10, ndigits);
 	f = floor(number * f) / f;
 	return DBL2NUM(f);
     }
-    f = floor(number);
-    if (!FIXABLE(f)) {
-	return rb_dbl2big(f);
+    else {
+	num = dbl2ival(floor(number));
+	if (ndigits < 0) num = rb_int_floor(num, ndigits);
+	return num;
     }
-    val = (long)f;
-    return LONG2FIX(val);
 }
 
 /*
@@ -2003,18 +2006,27 @@ flo_ceil(int argc, VALUE *argv, VALUE nu https://github.com/ruby/ruby/blob/trunk/numeric.c#L2006
     int ndigits = 0;
 
     if (rb_check_arity(argc, 0, 1)) {
-       ndigits = NUM2INT(argv[0]);
+	ndigits = NUM2INT(argv[0]);
     }
     number = RFLOAT_VALUE(num);
-    if (ndigits < 0) {
-	return rb_int_ceil(dbl2ival(ceil(number)), ndigits);
+    if (number == 0.0) {
+	return ndigits > 0 ? DBL2NUM(number) : INT2FIX(0);
     }
-    if (ndigits == 0) {
-	return dbl2ival(ceil(number));
+    if (ndigits > 0) {
+	int binexp;
+	frexp(number, &binexp);
+	if (float_round_overflow(ndigits, binexp)) return num;
+	if (number < 0.0 && float_round_underflow(ndigits, binexp))
+	    return DBL2NUM(0.0);
+	f = pow(10, ndigits);
+	f = ceil(number * f) / f;
+	return DBL2NUM(f);
+    }
+    else {
+	num = dbl2ival(ceil(number));
+	if (ndigits < 0) num = rb_int_ceil(num, ndigits);
+	return num;
     }
-    if (float_invariant_round(number, ndigits, &num)) return num;
-    f = pow(10, ndigits);
-    return DBL2NUM(ceil(number * f) / f);
 }
 
 static int
@@ -2252,27 +2264,33 @@ flo_round(int argc, VALUE *argv, VALUE n https://github.com/ruby/ruby/blob/trunk/numeric.c#L2264
 	ndigits = NUM2INT(nd);
     }
     mode = rb_num_get_rounding_option(opt);
+    number = RFLOAT_VALUE(num);
+    if (number == 0.0) {
+	return ndigits > 0 ? DBL2NUM(number) : INT2FIX(0);
+    }
     if (ndigits < 0) {
 	return rb_int_round(flo_to_i(num), ndigits, mode);
     }
-    number  = RFLOAT_VALUE(num);
     if (ndigits == 0) {
 	x = ROUND_CALL(mode, round, (number, 1.0));
 	return dbl2ival(x);
     }
-    if (float_invariant_round(number, ndigits, &num)) return num;
-    f = pow(10, ndigits);
-    x = ROUND_CALL(mode, round, (number, f));
-    return DBL2NUM(x / f);
+    if (isfinite(number)) {
+	int binexp;
+	frexp(number, &binexp);
+	if (float_round_overflow(ndigits, binexp)) return num;
+	if (float_round_underflow(ndigits, binexp)) return DBL2NUM(0);
+	f = pow(10, ndigits);
+	x = ROUND_CALL(mode, round, (number, f));
+	return DBL2NUM(x / f);
+    }
+    return num;
 }
 
 static int
-float_invariant_round(double number, int ndigits, VALUE *num)
+float_round_overflow(int ndigits, int binexp)
 {
     enum {float_dig = DBL_DIG+2};
-    int binexp;
-
-    frexp(number, &binexp);
 
 /* Let `exp` be such that `number` is written as:"0.#{digits}e#{exp}",
    i.e. such that  10 ** (exp - 1) <= |number| < 10 ** exp
@@ -2291,12 +2309,16 @@ float_invariant_round(double number, int https://github.com/ruby/ruby/blob/trunk/numeric.c#L2309
 	So if ndigits + floor(binexp/(4 or 3)) >= float_dig, the result is number
 	If ndigits + ceil(binexp/(3 or 4)) < 0 the result is 0
 */
-    if (isinf(number) || isnan(number) ||
-	(ndigits >= float_dig - (binexp > 0 ? binexp / 4 : binexp / 3 - 1))) {
+    if (ndigits >= float_dig - (binexp > 0 ? binexp / 4 : binexp / 3 - 1)) {
 	return TRUE;
     }
+    return FALSE;
+}
+
+static int
+float_round_underflow(int ndigits, int binexp)
+{
     if (ndigits < - (binexp > 0 ? binexp / 3 + 1 : binexp / 4)) {
-	*num = DBL2NUM(0);
 	return TRUE;
     }
     return FALSE;
Index: test/ruby/test_float.rb
===================================================================
--- test/ruby/test_float.rb	(revision 58912)
+++ test/ruby/test_float.rb	(revision 58913)
@@ -457,6 +457,8 @@ class TestFloat < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_float.rb#L457
   end
 
   def test_floor_with_precision
+    assert_equal(+0.0, +0.001.floor(1))
+    assert_equal(-0.1, -0.001.floor(1))
     assert_equal(1.100, 1.111.floor(1))
     assert_equal(1.110, 1.111.floor(2))
     assert_equal(11110, 11119.9.floor(-1))
@@ -484,6 +486,8 @@ class TestFloat < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_float.rb#L486
   end
 
   def test_ceil_with_precision
+    assert_equal(+0.1, +0.001.ceil(1))
+    assert_equal(-0.0, -0.001.ceil(1))
     assert_equal(1.200, 1.111.ceil(1))
     assert_equal(1.120, 1.111.ceil(2))
     assert_equal(11120, 11111.1.ceil(-1))

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]