diff options
author | Kenta Murata <mrkn@mrkn.jp> | 2021-01-13 10:28:23 +0900 |
---|---|---|
committer | Kenta Murata <mrkn@mrkn.jp> | 2021-01-13 11:49:18 +0900 |
commit | a5b4b806de3b130348a92aa3306fbb9318efb762 (patch) | |
tree | f4786635a2c3ba9b7c98588542f60264add5b70d /ext/bigdecimal | |
parent | 30f13164011dd876fd95a0e3fcd7c1224c4e04b5 (diff) | |
download | ruby-a5b4b806de3b130348a92aa3306fbb9318efb762.tar.gz |
[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
Diffstat (limited to 'ext/bigdecimal')
-rw-r--r-- | ext/bigdecimal/bigdecimal.c | 53 | ||||
-rw-r--r-- | ext/bigdecimal/lib/bigdecimal/util.rb | 2 |
2 files changed, 33 insertions, 22 deletions
diff --git a/ext/bigdecimal/bigdecimal.c b/ext/bigdecimal/bigdecimal.c index 2d3f09d562..f48f3edb18 100644 --- a/ext/bigdecimal/bigdecimal.c +++ b/ext/bigdecimal/bigdecimal.c @@ -947,7 +947,7 @@ BigDecimal_coerce(VALUE self, VALUE other) 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) 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) 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) 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) 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) 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) 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) 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) 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) 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) 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) } /* 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) 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: 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 00a3e967bd..cb645d2a71 100644 --- a/ext/bigdecimal/lib/bigdecimal/util.rb +++ b/ext/bigdecimal/lib/bigdecimal/util.rb @@ -43,7 +43,7 @@ class Float < Numeric # # See also BigDecimal::new. # - def to_d(precision=Float::DIG+1) + def to_d(precision=0) BigDecimal(self, precision) end end |