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

ruby-changes:64851

From: Kenta <ko1@a...>
Date: Wed, 13 Jan 2021 11:49:35 +0900 (JST)
Subject: [ruby-changes:64851] a5b4b806de (master): [ruby/bigdecimal] Allow digits=0 in BigDecimal(flt) and Float#to_d

https://git.ruby-lang.org/ruby.git/commit/?id=a5b4b806de

From a5b4b806de3b130348a92aa3306fbb9318efb762 Mon Sep 17 00:00:00 2001
From: Kenta Murata <mrkn@m...>
Date: Wed, 13 Jan 2021 10:28:23 +0900
Subject: [ruby/bigdecimal] Allow digits=0 in BigDecimal(flt) and Float#to_d

Using dtoa of mode=0, we can determine the number of digits in decimal that is
necessary to represent the given Float number without errors.

This change permits digits=0 in BigDecimal(flt) and Float#to_d, and these
methods use dtoa of mode=0 when the given digits is 0.

Internal implicit conversion from Float also uses digits=0.

[Fix GH-70]

https://github.com/ruby/bigdecimal/commit/2dbe170e35

diff --git a/ext/bigdecimal/bigdecimal.c b/ext/bigdecimal/bigdecimal.c
index 2d3f09d..f48f3ed 100644
--- a/ext/bigdecimal/bigdecimal.c
+++ b/ext/bigdecimal/bigdecimal.c
@@ -947,7 +947,7 @@ BigDecimal_coerce(VALUE self, VALUE other) https://github.com/ruby/ruby/blob/trunk/ext/bigdecimal/bigdecimal.c#L947
     Real *b;
 
     if (RB_TYPE_P(other, T_FLOAT)) {
-	GUARD_OBJ(b, GetVpValueWithPrec(other, DBLE_FIG, 1));
+	GUARD_OBJ(b, GetVpValueWithPrec(other, 0, 1));
         obj = rb_assoc_new(VpCheckGetValue(b), self);
     }
     else {
@@ -1005,7 +1005,7 @@ BigDecimal_add(VALUE self, VALUE r) https://github.com/ruby/ruby/blob/trunk/ext/bigdecimal/bigdecimal.c#L1005
 
     GUARD_OBJ(a, GetVpValue(self, 1));
     if (RB_TYPE_P(r, T_FLOAT)) {
-	b = GetVpValueWithPrec(r, DBLE_FIG, 1);
+	b = GetVpValueWithPrec(r, 0, 1);
     }
     else if (RB_TYPE_P(r, T_RATIONAL)) {
 	b = GetVpValueWithPrec(r, a->Prec*VpBaseFig(), 1);
@@ -1063,7 +1063,7 @@ BigDecimal_sub(VALUE self, VALUE r) https://github.com/ruby/ruby/blob/trunk/ext/bigdecimal/bigdecimal.c#L1063
 
     GUARD_OBJ(a, GetVpValue(self,1));
     if (RB_TYPE_P(r, T_FLOAT)) {
-	b = GetVpValueWithPrec(r, DBLE_FIG, 1);
+	b = GetVpValueWithPrec(r, 0, 1);
     }
     else if (RB_TYPE_P(r, T_RATIONAL)) {
 	b = GetVpValueWithPrec(r, a->Prec*VpBaseFig(), 1);
@@ -1113,7 +1113,7 @@ BigDecimalCmp(VALUE self, VALUE r,char op) https://github.com/ruby/ruby/blob/trunk/ext/bigdecimal/bigdecimal.c#L1113
 	break;
 
     case T_FLOAT:
-	GUARD_OBJ(b, GetVpValueWithPrec(r, DBLE_FIG, 0));
+	GUARD_OBJ(b, GetVpValueWithPrec(r, 0, 0));
 	break;
 
     case T_RATIONAL:
@@ -1326,7 +1326,7 @@ BigDecimal_mult(VALUE self, VALUE r) https://github.com/ruby/ruby/blob/trunk/ext/bigdecimal/bigdecimal.c#L1326
 
     GUARD_OBJ(a, GetVpValue(self, 1));
     if (RB_TYPE_P(r, T_FLOAT)) {
-        b = GetVpValueWithPrec(r, DBLE_FIG, 1);
+        b = GetVpValueWithPrec(r, 0, 1);
     }
     else if (RB_TYPE_P(r, T_RATIONAL)) {
 	b = GetVpValueWithPrec(r, a->Prec*VpBaseFig(), 1);
@@ -1354,7 +1354,7 @@ BigDecimal_divide(Real **c, Real **res, Real **div, VALUE self, VALUE r) https://github.com/ruby/ruby/blob/trunk/ext/bigdecimal/bigdecimal.c#L1354
 
     GUARD_OBJ(a, GetVpValue(self, 1));
     if (RB_TYPE_P(r, T_FLOAT)) {
-        b = GetVpValueWithPrec(r, DBLE_FIG, 1);
+        b = GetVpValueWithPrec(r, 0, 1);
     }
     else if (RB_TYPE_P(r, T_RATIONAL)) {
 	b = GetVpValueWithPrec(r, a->Prec*VpBaseFig(), 1);
@@ -1420,7 +1420,7 @@ BigDecimal_DoDivmod(VALUE self, VALUE r, Real **div, Real **mod) https://github.com/ruby/ruby/blob/trunk/ext/bigdecimal/bigdecimal.c#L1420
 
     GUARD_OBJ(a, GetVpValue(self, 1));
     if (RB_TYPE_P(r, T_FLOAT)) {
-	b = GetVpValueWithPrec(r, DBLE_FIG, 1);
+	b = GetVpValueWithPrec(r, 0, 1);
     }
     else if (RB_TYPE_P(r, T_RATIONAL)) {
 	b = GetVpValueWithPrec(r, a->Prec*VpBaseFig(), 1);
@@ -1521,7 +1521,7 @@ BigDecimal_divremain(VALUE self, VALUE r, Real **dv, Real **rv) https://github.com/ruby/ruby/blob/trunk/ext/bigdecimal/bigdecimal.c#L1521
 
     GUARD_OBJ(a, GetVpValue(self, 1));
     if (RB_TYPE_P(r, T_FLOAT)) {
-	b = GetVpValueWithPrec(r, DBLE_FIG, 1);
+	b = GetVpValueWithPrec(r, 0, 1);
     }
     else if (RB_TYPE_P(r, T_RATIONAL)) {
 	b = GetVpValueWithPrec(r, a->Prec*VpBaseFig(), 1);
@@ -2416,7 +2416,7 @@ BigDecimal_power(int argc, VALUE*argv, VALUE self) https://github.com/ruby/ruby/blob/trunk/ext/bigdecimal/bigdecimal.c#L2416
         if (NIL_P(prec)) {
             n += DBLE_FIG;
         }
-        exp = GetVpValueWithPrec(vexp, DBLE_FIG, 1);
+        exp = GetVpValueWithPrec(vexp, 0, 1);
 	break;
 
       case T_RATIONAL:
@@ -2829,7 +2829,8 @@ rb_float_convert_to_BigDecimal(VALUE val, size_t digs, int raise_exception) https://github.com/ruby/ruby/blob/trunk/ext/bigdecimal/bigdecimal.c#L2829
     char buf[DBLE_FIG + BASE_FIG + 2 + 1];
     int decpt, negative_p;
     char *e;
-    char *p = BigDecimal_dtoa(d, 2, digs, &decpt, &negative_p, &e);
+    const int mode = digs == 0 ? 0 : 2;
+    char *p = BigDecimal_dtoa(d, mode, digs, &decpt, &negative_p, &e);
     int len10 = (int)(e - p);
     if (len10 >= (int)sizeof(buf))
         len10 = (int)sizeof(buf) - 1;
@@ -3006,6 +3007,7 @@ rb_convert_to_BigDecimal(VALUE val, size_t digs, int raise_exception) https://github.com/ruby/ruby/blob/trunk/ext/bigdecimal/bigdecimal.c#L3007
 
         VALUE copy = TypedData_Wrap_Struct(rb_cBigDecimal, &BigDecimal_data_type, 0);
         vp = VpCopy(NULL, vp);
+        /* TODO: rounding */
         BigDecimal_wrap_struct(copy, vp);
         return VpCheckGetValue(vp);
     }
@@ -3046,19 +3048,28 @@ rb_convert_to_BigDecimal(VALUE val, size_t digs, int raise_exception) https://github.com/ruby/ruby/blob/trunk/ext/bigdecimal/bigdecimal.c#L3048
 }
 
 /* call-seq:
- *   BigDecimal(initial, digits=0, exception: true)
+ *   BigDecimal(arg, exception: true)
+ *   BigDecimal(arg, digits, exception: true)
  *
- * Create a new BigDecimal object.
+ * Returns <i>arg</i> converted to a BigDecimal.  Numeric types are converted
+ * directly.  Other types except for String are first converted to String
+ * by <code>to_str</code>.  Strings can be converted when it has appropriate
+ * forms of decimal numbers.  Exceptions can be suppressed by passing
+ * <code>exception: false</code>.
  *
- * initial:: The initial value, as an Integer, a Float, a Rational,
- *           a BigDecimal, or a String.
+ * When <i>arg</i> is a Float and <i>digits</i> is <code>0</code>, the number
+ * of digits is determined by the algorithm of <code>dtoa</code> function
+ * written by David M. Gay.  That algorithm is based on "How to Print Floating-
+ * Point Numbers Accurately" by Guy L. Steele, Jr. and Jon L. White [Proc. ACM
+ * SIGPLAN '90, pp. 112-126].
  *
- *           If it is a String, spaces are ignored and unrecognized characters
- *           terminate the value.
+ * arg:: The value converted to a BigDecimal.
  *
- * digits:: The number of significant digits, as an Integer. If omitted or 0,
- *          the number of significant digits is determined from the initial
- *          value.
+ *       If it is a String, spaces are ignored and unrecognized characters
+ *       terminate the value.
+ *
+ * digits:: The number of significant digits, as an Integer. If omitted,
+ *          the number of significant digits is determined from <i>arg</i>.
  *
  *          The actual number of significant digits used in computation is
  *          usually larger than the specified number.
@@ -3303,7 +3314,7 @@ BigMath_s_exp(VALUE klass, VALUE x, VALUE vprec) https://github.com/ruby/ruby/blob/trunk/ext/bigdecimal/bigdecimal.c#L3314
 	infinite = isinf(flo);
 	nan = isnan(flo);
 	if (!infinite && !nan) {
-	    vx = GetVpValueWithPrec(x, DBLE_FIG, 0);
+	    vx = GetVpValueWithPrec(x, 0, 0);
 	}
 	break;
 
@@ -3456,7 +3467,7 @@ get_vp_value: https://github.com/ruby/ruby/blob/trunk/ext/bigdecimal/bigdecimal.c#L3467
 	infinite = isinf(flo);
 	nan = isnan(flo);
 	if (!zero && !negative && !infinite && !nan) {
-	    vx = GetVpValueWithPrec(x, DBLE_FIG, 1);
+	    vx = GetVpValueWithPrec(x, 0, 1);
 	}
 	break;
 
diff --git a/ext/bigdecimal/lib/bigdecimal/util.rb b/ext/bigdecimal/lib/bigdecimal/util.rb
index 00a3e96..cb645d2 100644
--- a/ext/bigdecimal/lib/bigdecimal/util.rb
+++ b/ext/bigdecimal/lib/bigdecimal/util.rb
@@ -43,7 +43,7 @@ class Float < Numeric https://github.com/ruby/ruby/blob/trunk/ext/bigdecimal/lib/bigdecimal/util.rb#L43
   #
   # See also BigDecimal::new.
   #
-  def to_d(precision=Float::DIG+1)
+  def to_d(precision=0)
     BigDecimal(self, precision)
   end
 end
diff --git a/test/bigdecimal/test_bigdecimal.rb b/test/bigdecimal/test_bigdecimal.rb
index e8fd85a..0063fb9 100644
--- a/test/bigdecimal/test_bigdecimal.rb
+++ b/test/bigdecimal/test_bigdecimal.rb
@@ -917,6 +917,7 @@ class TestBigDecimal < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/bigdecimal/test_bigdecimal.rb#L917
 
   def test_mult_with_float
     assert_kind_of(BigDecimal, BigDecimal("3") * 1.5)
+    assert_equal(BigDecimal("64.4"), BigDecimal(1) * 64.4)
   end
 
   def test_mult_with_rational
@@ -955,6 +956,7 @@ class TestBigDecimal < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/bigdecimal/test_bigdecimal.rb#L956
 
   def test_div_with_float
     assert_kind_of(BigDecimal, BigDecimal("3") / 1.5)
+    assert_equal(BigDecimal("0.5"), BigDecimal(1) / 2.0)
   end
 
   def test_div_with_rational
diff --git a/test/bigdecimal/test_bigdecimal_util.rb b/test/bigdecimal/test_bigdecimal_util.rb
index 9c6973d..ffd4c5f 100644
--- a/test/bigdecimal/test_bigdecimal_util.rb
+++ b/test/bigdecimal/test_bigdecimal_util.rb
@@ -19,11 +19,11 @@ class TestBigDecimalUtil < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/bigdecimal/test_bigdecimal_util.rb#L19
 
   def test_Float_to_d_without_precision
     delta = 1.0/10**(Float::DIG+1)
-    assert_in_delta(BigDecimal(0.5, Float::DIG+1), 0.5.to_d, delta)
-    assert_in_delta(BigDecimal(355.0/113.0, Float::DIG+1), (355.0/113.0).to_d, delta)
+    assert_in_delta(BigDecimal(0.5, 0), 0.5.to_d, delta)
+    assert_in_delta(BigDecimal(355.0/113.0, 0), (355.0/113.0).to_d, delta)
 
     assert_equal(9.05, 9.05.to_d.to_f)
-    assert_equal("9.050000000000001", 9.05.to_d.to_s('F'))
+    assert_equal("9.05", 9.05.to_d.to_s('F'))
 
     assert_equal(Math::PI, Math::PI.to_d.to_f)
  (... truncated)

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

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