From 2810c12a99fed7297d558d8b1aaaf755eb8b8a2d Mon Sep 17 00:00:00 2001 From: mrkn Date: Wed, 5 Dec 2018 11:30:24 +0000 Subject: Import bigdecimal 1.4.0.pre.20181205a * https://github.com/ruby/bigdecimal/compare/74d25ef..v1.4.0.pre.20181205a git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@66222 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- NEWS | 19 + ext/bigdecimal/bigdecimal.c | 524 ++++++++++++++---------- ext/bigdecimal/bigdecimal.def | 3 + ext/bigdecimal/bigdecimal.gemspec | 11 +- ext/bigdecimal/bigdecimal.h | 2 +- ext/bigdecimal/depend | 2 + ext/bigdecimal/extconf.rb | 4 +- ext/bigdecimal/lib/bigdecimal/jacobian.rb | 3 + ext/bigdecimal/lib/bigdecimal/util.rb | 30 +- ext/bigdecimal/sample/linear.rb | 21 +- ext/bigdecimal/sample/nlsolve.rb | 10 +- ext/bigdecimal/util/extconf.rb | 24 ++ ext/bigdecimal/util/util.c | 9 + spec/ruby/library/bigdecimal/BigDecimal_spec.rb | 43 +- spec/ruby/library/bigdecimal/ver_spec.rb | 11 - test/bigdecimal/test_bigdecimal.rb | 185 +++++---- test/bigdecimal/test_bigdecimal_util.rb | 32 +- 17 files changed, 590 insertions(+), 343 deletions(-) create mode 100644 ext/bigdecimal/bigdecimal.def create mode 100644 ext/bigdecimal/util/extconf.rb create mode 100644 ext/bigdecimal/util/util.c delete mode 100644 spec/ruby/library/bigdecimal/ver_spec.rb diff --git a/NEWS b/NEWS index 3cf998b9da..139197fd94 100644 --- a/NEWS +++ b/NEWS @@ -331,6 +331,11 @@ sufficient information, see the ChangeLog file or Redmine * Use 1.17.1. It's latest stable version. +[BigDecimal] + + Update to the version 1.4.0. This version includes several compatibility + issues, see Compatibility issues section below for the detail. + [Coverage] A oneshot_lines mode is added. [Feature #15022] @@ -489,6 +494,20 @@ sufficient information, see the ChangeLog file or Redmine * thwait * tracer +[BigDecimal] + + * The following methods are removed. + + * BigDecimal.allocate + * BigDecimal.new + * BigDecimal.ver + + * Every BigDecimal object is frozen. [Feature #13984] + + * BigDecimal() parses the given string like Float(). + + * String#to_d parses the receiver string like String#to_f. + === C API updates === Supported platform changes diff --git a/ext/bigdecimal/bigdecimal.c b/ext/bigdecimal/bigdecimal.c index d1aa56c121..64d7f20078 100644 --- a/ext/bigdecimal/bigdecimal.c +++ b/ext/bigdecimal/bigdecimal.c @@ -135,24 +135,6 @@ rb_rational_den(VALUE rat) */ #define DoSomeOne(x,y,f) rb_num_coerce_bin(x,y,f) -/* - * Returns the BigDecimal version number. - */ -static VALUE -BigDecimal_version(VALUE self) -{ - /* - * 1.0.0: Ruby 1.8.0 - * 1.0.1: Ruby 1.8.1 - * 1.1.0: Ruby 1.9.3 - */ -#ifndef RUBY_BIGDECIMAL_VERSION -# error RUBY_BIGDECIMAL_VERSION is not defined -#endif - rb_warning("BigDecimal.ver is deprecated; use BigDecimal::VERSION instead."); - return rb_str_new2(RUBY_BIGDECIMAL_VERSION); -} - /* * VP routines used in BigDecimal part */ @@ -664,9 +646,10 @@ VP_EXPORT Real * VpNewRbClass(size_t mx, const char *str, VALUE klass) { VALUE obj = TypedData_Wrap_Struct(klass, &BigDecimal_data_type, 0); - Real *pv = VpAlloc(mx,str); + Real *pv = VpAlloc(mx, str, 1, 1); RTYPEDDATA_DATA(obj) = pv; pv->obj = obj; + RB_OBJ_FREEZE(obj); return pv; } @@ -2165,15 +2148,10 @@ BigDecimal_exponent(VALUE self) return INT2NUM(e); } -/* Returns debugging information about the value as a string of comma-separated - * values in angle brackets with a leading #: +/* Returns a string representation of self. * * BigDecimal("1234.5678").inspect * #=> "0.12345678e4" - * - * The first part is the address, the second is the value as a string, and - * the final part ss(mm) is the current number of significant digits and the - * maximum number of significant digits, respectively. */ static VALUE BigDecimal_inspect(VALUE self) @@ -2335,7 +2313,7 @@ BigDecimal_power(int argc, VALUE*argv, VALUE self) n = NIL_P(prec) ? (ssize_t)(x->Prec*VpBaseFig()) : NUM2SSIZET(prec); if (VpIsNaN(x)) { - y = VpCreateRbObject(n, "0#"); + y = VpCreateRbObject(n, "0"); RB_GC_GUARD(y->obj); VpSetNaN(y); return ToValue(y); @@ -2459,7 +2437,7 @@ BigDecimal_power(int argc, VALUE*argv, VALUE self) } } else { - y = VpCreateRbObject(n, "0#"); + y = VpCreateRbObject(n, "0"); if (BIGDECIMAL_NEGATIVE_P(x)) { if (is_integer(vexp)) { if (is_even(vexp)) { @@ -2492,7 +2470,7 @@ BigDecimal_power(int argc, VALUE*argv, VALUE self) } else if (RTEST(rb_funcall(abs_value, '<', 1, INT2FIX(1)))) { if (is_negative(vexp)) { - y = VpCreateRbObject(n, "0#"); + y = VpCreateRbObject(n, "0"); if (is_even(vexp)) { VpSetInf(y, VpGetSign(x)); } @@ -2510,7 +2488,7 @@ BigDecimal_power(int argc, VALUE*argv, VALUE self) } else { if (is_positive(vexp)) { - y = VpCreateRbObject(n, "0#"); + y = VpCreateRbObject(n, "0"); if (is_even(vexp)) { VpSetInf(y, VpGetSign(x)); } @@ -2560,72 +2538,6 @@ BigDecimal_power_op(VALUE self, VALUE exp) return BigDecimal_power(1, &exp, self); } -static VALUE -BigDecimal_s_allocate(VALUE klass) -{ - return VpNewRbClass(0, NULL, klass)->obj; -} - -static Real *BigDecimal_new(int argc, VALUE *argv); - -/* call-seq: - * new(initial, digits) - * - * Create a new BigDecimal object. - * - * initial:: The initial value, as an Integer, a Float, a Rational, - * a BigDecimal, or a String. - * - * 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 or 0, - * the number of significant digits is determined from the initial - * value. - * - * The actual number of significant digits used in computation is usually - * larger than the specified number. - * - * ==== Exceptions - * - * TypeError:: If the +initial+ type is neither Integer, Float, - * Rational, nor BigDecimal, this exception is raised. - * - * TypeError:: If the +digits+ is not an Integer, this exception is raised. - * - * ArgumentError:: If +initial+ is a Float, and the +digits+ is larger than - * Float::DIG + 1, this exception is raised. - * - * ArgumentError:: If the +initial+ is a Float or Rational, and the +digits+ - * value is omitted, this exception is raised. - */ -static VALUE -BigDecimal_s_new(int argc, VALUE *argv, VALUE self) -{ - rb_warning("BigDecimal.new is deprecated; use Kernel.BigDecimal method instead."); - return rb_call_super(argc, argv); -} - -static VALUE -BigDecimal_initialize(int argc, VALUE *argv, VALUE self) -{ - ENTER(1); - Real *pv = rb_check_typeddata(self, &BigDecimal_data_type); - Real *x; - - GUARD_OBJ(x, BigDecimal_new(argc, argv)); - if (ToValue(x)) { - pv = VpCopy(pv, x); - } - else { - VpFree(pv); - pv = x; - } - DATA_PTR(self) = pv; - pv->obj = self; - return self; -} - /* :nodoc: * * private method for dup and clone the provided BigDecimal +other+ @@ -2648,19 +2560,60 @@ BigDecimal_clone(VALUE self) return self; } +static int +opts_exception_p(VALUE opts) +{ + static ID kwds[1]; + VALUE exception; + if (!kwds[0]) { + kwds[0] = rb_intern_const("exception"); + } + rb_get_kwargs(opts, kwds, 0, 1, &exception); + return exception != Qfalse; +} + static Real * BigDecimal_new(int argc, VALUE *argv) { size_t mf; + VALUE opts = Qnil; VALUE nFig; VALUE iniValue; double d; + int exc; + + argc = rb_scan_args(argc, argv, "11:", &iniValue, &nFig, &opts); + exc = opts_exception_p(opts); - if (rb_scan_args(argc, argv, "11", &iniValue, &nFig) == 1) { + if (argc == 1) { mf = 0; } else { - mf = GetPrecisionInt(nFig); + /* expand GetPrecisionInt for exception suppression */ + ssize_t n = NUM2INT(nFig); + if (n < 0) { + if (!exc) { + return NULL; + } + rb_raise(rb_eArgError, "negative precision"); + } + mf = (size_t)n; + } + + if (SPECIAL_CONST_P(iniValue)) { + switch (iniValue) { + case Qnil: + if (!exc) return NULL; + rb_raise(rb_eTypeError, "can't convert nil into BigDecimal"); + case Qtrue: + if (!exc) return NULL; + rb_raise(rb_eTypeError, "can't convert true into BigDecimal"); + case Qfalse: + if (!exc) return NULL; + rb_raise(rb_eTypeError, "can't convert false into BigDecimal"); + default: + break; + } } switch (TYPE(iniValue)) { @@ -2683,11 +2636,17 @@ BigDecimal_new(int argc, VALUE *argv) return pv; } if (mf > DBL_DIG+1) { + if (!exc) { + return NULL; + } rb_raise(rb_eArgError, "precision too large."); } /* fall through */ case T_RATIONAL: if (NIL_P(nFig)) { + if (!exc) { + return NULL; + } rb_raise(rb_eArgError, "can't omit precision for a %"PRIsVALUE".", RB_OBJ_CLASSNAME(iniValue)); @@ -2699,22 +2658,60 @@ BigDecimal_new(int argc, VALUE *argv) default: break; } + /* TODO: support to_d */ + if (!exc) { + iniValue = rb_check_convert_type(iniValue, T_STRING, "String", "to_str"); + if (NIL_P(iniValue)) return NULL; + } StringValueCStr(iniValue); - return VpAlloc(mf, RSTRING_PTR(iniValue)); + return VpAlloc(mf, RSTRING_PTR(iniValue), 1, exc); } -/* See also BigDecimal.new */ +/* call-seq: + * BigDecimal(initial, digits) + * + * Create a new BigDecimal object. + * + * initial:: The initial value, as an Integer, a Float, a Rational, + * a BigDecimal, or a String. + * + * 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 or 0, + * the number of significant digits is determined from the initial + * value. + * + * The actual number of significant digits used in computation is usually + * larger than the specified number. + * + * ==== Exceptions + * + * TypeError:: If the +initial+ type is neither Integer, Float, + * Rational, nor BigDecimal, this exception is raised. + * + * TypeError:: If the +digits+ is not an Integer, this exception is raised. + * + * ArgumentError:: If +initial+ is a Float, and the +digits+ is larger than + * Float::DIG + 1, this exception is raised. + * + * ArgumentError:: If the +initial+ is a Float or Rational, and the +digits+ + * value is omitted, this exception is raised. + */ static VALUE -BigDecimal_global_new(int argc, VALUE *argv, VALUE self) +f_BigDecimal(int argc, VALUE *argv, VALUE self) { ENTER(1); Real *pv; VALUE obj; obj = TypedData_Wrap_Struct(rb_cBigDecimal, &BigDecimal_data_type, 0); - GUARD_OBJ(pv, BigDecimal_new(argc, argv)); + pv = BigDecimal_new(argc, argv); + if (pv == NULL) return Qnil; + SAVE(pv); if (ToValue(pv)) pv = VpCopy(NULL, pv); RTYPEDDATA_DATA(obj) = pv; + RB_OBJ_FREEZE(obj); return pv->obj = obj; } @@ -3138,6 +3135,20 @@ get_vp_value: return y; } +VALUE +rmpd_util_str_to_d(VALUE str) +{ + ENTER(1); + char const *c_str; + Real *pv; + + c_str = StringValueCStr(str); + GUARD_OBJ(pv, VpAlloc(0, c_str, 0, 1)); + pv->obj = TypedData_Wrap_Struct(rb_cBigDecimal, &BigDecimal_data_type, pv); + RB_OBJ_FREEZE(pv->obj); + return pv->obj; +} + /* Document-class: BigDecimal * BigDecimal provides arbitrary-precision floating point decimal arithmetic. * @@ -3277,18 +3288,17 @@ Init_bigdecimal(void) /* Class and method registration */ rb_cBigDecimal = rb_define_class("BigDecimal", rb_cNumeric); - rb_define_alloc_func(rb_cBigDecimal, BigDecimal_s_allocate); /* Global function */ - rb_define_global_function("BigDecimal", BigDecimal_global_new, -1); + rb_define_global_function("BigDecimal", f_BigDecimal, -1); /* Class methods */ - rb_define_singleton_method(rb_cBigDecimal, "new", BigDecimal_s_new, -1); + rb_undef_method(CLASS_OF(rb_cBigDecimal), "allocate"); + rb_undef_method(CLASS_OF(rb_cBigDecimal), "new"); rb_define_singleton_method(rb_cBigDecimal, "mode", BigDecimal_mode, -1); rb_define_singleton_method(rb_cBigDecimal, "limit", BigDecimal_limit, -1); rb_define_singleton_method(rb_cBigDecimal, "double_fig", BigDecimal_double_fig, 0); rb_define_singleton_method(rb_cBigDecimal, "_load", BigDecimal_load, 1); - rb_define_singleton_method(rb_cBigDecimal, "ver", BigDecimal_version, 0); rb_define_singleton_method(rb_cBigDecimal, "save_exception_mode", BigDecimal_save_exception_mode, 0); rb_define_singleton_method(rb_cBigDecimal, "save_rounding_mode", BigDecimal_save_rounding_mode, 0); @@ -3408,14 +3418,13 @@ Init_bigdecimal(void) arg = rb_str_new2("+Infinity"); /* Positive infinity value. */ - rb_define_const(rb_cBigDecimal, "INFINITY", BigDecimal_global_new(1, &arg, rb_cBigDecimal)); + rb_define_const(rb_cBigDecimal, "INFINITY", f_BigDecimal(1, &arg, rb_cBigDecimal)); arg = rb_str_new2("NaN"); /* 'Not a Number' value. */ - rb_define_const(rb_cBigDecimal, "NAN", BigDecimal_global_new(1, &arg, rb_cBigDecimal)); + rb_define_const(rb_cBigDecimal, "NAN", f_BigDecimal(1, &arg, rb_cBigDecimal)); /* instance methods */ - rb_define_method(rb_cBigDecimal, "initialize", BigDecimal_initialize, -1); rb_define_method(rb_cBigDecimal, "initialize_copy", BigDecimal_initialize_copy, 1); rb_define_method(rb_cBigDecimal, "precs", BigDecimal_prec, 0); @@ -3714,13 +3723,7 @@ VpSetRoundMode(unsigned short n) * (to let the compiler know they may be changed in outside * (... but not actually..)). */ -volatile const double gZero_ABCED9B1_CE73__00400511F31D = 0.0; volatile const double gOne_ABCED9B4_CE73__00400511F31D = 1.0; -static double -Zero(void) -{ - return gZero_ABCED9B1_CE73__00400511F31D; -} static double One(void) @@ -3745,25 +3748,19 @@ One(void) VP_EXPORT double VpGetDoubleNaN(void) /* Returns the value of NaN */ { - static double fNaN = 0.0; - if (fNaN == 0.0) fNaN = Zero()/Zero(); - return fNaN; + return nan(""); } VP_EXPORT double VpGetDoublePosInf(void) /* Returns the value of +Infinity */ { - static double fInf = 0.0; - if (fInf == 0.0) fInf = One()/Zero(); - return fInf; + return HUGE_VAL; } VP_EXPORT double VpGetDoubleNegInf(void) /* Returns the value of -Infinity */ { - static double fInf = 0.0; - if (fInf == 0.0) fInf = -(One()/Zero()); - return fInf; + return -HUGE_VAL; } VP_EXPORT double @@ -3964,8 +3961,8 @@ VpInit(BDIGIT BaseVal) VpGetDoubleNegZero(); /* Allocates Vp constants. */ - VpConstOne = VpAlloc(1UL, "1"); - VpPt5 = VpAlloc(1UL, ".5"); + VpConstOne = VpAlloc(1UL, "1", 1, 1); + VpPt5 = VpAlloc(1UL, ".5", 1, 1); #ifdef BIGDECIMAL_DEBUG gnAlloc = 0; @@ -4029,6 +4026,52 @@ overflow: return VpException(VP_EXCEPTION_OVERFLOW, "Exponent overflow", 0); } +Real * +rmpd_parse_special_string(const char *str) +{ + static const struct { + const char *str; + size_t len; + int sign; + } table[] = { + { SZ_INF, sizeof(SZ_INF) - 1, VP_SIGN_POSITIVE_INFINITE }, + { SZ_PINF, sizeof(SZ_PINF) - 1, VP_SIGN_POSITIVE_INFINITE }, + { SZ_NINF, sizeof(SZ_NINF) - 1, VP_SIGN_NEGATIVE_INFINITE }, + { SZ_NaN, sizeof(SZ_NaN) - 1, VP_SIGN_NaN } + }; + static const size_t table_length = sizeof(table) / sizeof(table[0]); + size_t i; + + for (i = 0; i < table_length; ++i) { + const char *p; + if (strncmp(str, table[i].str, table[i].len) != 0) { + continue; + } + + p = str + table[i].len; + while (*p && ISSPACE(*p)) ++p; + if (*p == '\0') { + Real *vp = VpAllocReal(1); + vp->MaxPrec = 1; + switch (table[i].sign) { + default: + UNREACHABLE; break; + case VP_SIGN_POSITIVE_INFINITE: + VpSetPosInf(vp); + return vp; + case VP_SIGN_NEGATIVE_INFINITE: + VpSetNegInf(vp); + return vp; + case VP_SIGN_NaN: + VpSetNaN(vp); + return vp; + } + } + } + + return NULL; +} + /* * Allocates variable. * [Input] @@ -4043,10 +4086,10 @@ overflow: * NULL be returned if memory allocation is failed,or any error. */ VP_EXPORT Real * -VpAlloc(size_t mx, const char *szVal) +VpAlloc(size_t mx, const char *szVal, int strict_p, int exc) { const char *orig_szVal = szVal; - size_t i, ni, ipn, ipf, nf, ipe, ne, dot_seen, exp_seen, nalloc; + size_t i, j, ni, ipf, nf, ipe, ne, dot_seen, exp_seen, nalloc; char v, *psz; int sign=1; Real *vp = NULL; @@ -4057,7 +4100,10 @@ VpAlloc(size_t mx, const char *szVal) if (mx == 0) ++mx; if (szVal) { + /* Skipping leading spaces */ while (ISSPACE(*szVal)) szVal++; + + /* Processing the leading one `#` */ if (*szVal != '#') { if (mf) { mf = (mf + BASE_FIG - 1) / BASE_FIG + 2; /* Needs 1 more for div */ @@ -4071,6 +4117,7 @@ VpAlloc(size_t mx, const char *szVal) } } else { + return_zero: /* necessary to be able to store */ /* at least mx digits. */ /* szVal==NULL ==> allocate zero value. */ @@ -4081,105 +4128,168 @@ VpAlloc(size_t mx, const char *szVal) return vp; } - /* Skip all '_' after digit: 2006-6-30 */ - ni = 0; + /* Check on Inf & NaN */ + if ((vp = rmpd_parse_special_string(szVal)) != NULL) { + return vp; + } + + /* Scanning digits */ + + /* A buffer for keeping scanned digits */ buf = rb_str_tmp_new(strlen(szVal) + 1); psz = RSTRING_PTR(buf); - i = 0; - ipn = 0; - while ((psz[i] = szVal[ipn]) != 0) { - if (ISSPACE(psz[i])) { - psz[i] = 0; + + /* cursor: i for psz, and j for szVal */ + i = j = 0; + + /* Scanning: sign part */ + v = psz[i] = szVal[j]; + if ((v == '-') || (v == '+')) { + sign = -(v == '-'); + ++i; + ++j; + } + + /* Scanning: integer part */ + ni = 0; /* number of digits in the integer part */ + while ((v = psz[i] = szVal[j]) != '\0') { + if (!strict_p && ISSPACE(v)) { + v = psz[i] = '\0'; break; } - if (ISDIGIT(psz[i])) ++ni; - if (psz[i] == '_') { + if (v == '_') { if (ni > 0) { - ipn++; - continue; + v = szVal[j+1]; + if (v == '\0' || ISSPACE(v) || ISDIGIT(v)) { + ++j; + continue; + } + if (!strict_p) { + v = psz[i] = '\0'; + break; + } } - psz[i] = 0; + goto invalid_value; + } + if (!ISDIGIT(v)) { break; } + ++ni; ++i; - ++ipn; - } - szVal = psz; - - /* Check on Inf & NaN */ - if (StrCmp(szVal, SZ_PINF) == 0 || StrCmp(szVal, SZ_INF) == 0 ) { - vp = VpAllocReal(1); - vp->MaxPrec = 1; /* set max precision */ - VpSetPosInf(vp); - return vp; - } - if (StrCmp(szVal, SZ_NINF) == 0) { - vp = VpAllocReal(1); - vp->MaxPrec = 1; /* set max precision */ - VpSetNegInf(vp); - return vp; - } - if (StrCmp(szVal, SZ_NaN) == 0) { - vp = VpAllocReal(1); - vp->MaxPrec = 1; /* set max precision */ - VpSetNaN(vp); - return vp; + ++j; } - /* check on number szVal[] */ - ipn = i = 0; - if (szVal[i] == '-') { sign=-1; ++i; } - else if (szVal[i] == '+') ++i; - /* Skip digits */ - ni = 0; /* digits in mantissa */ - while ((v = szVal[i]) != 0) { - if (!ISDIGIT(v)) break; - ++i; - ++ni; - } - nf = 0; - ipf = 0; - ipe = 0; - ne = 0; + /* Scanning: fractional part */ + nf = 0; /* number of digits in the fractional part */ + ne = 0; /* number of digits in the exponential part */ + ipf = 0; /* index of the beginning of the fractional part */ + ipe = 0; /* index of the beginning of the exponential part */ dot_seen = 0; exp_seen = 0; - if (v) { - /* other than digit nor \0 */ - if (szVal[i] == '.') { /* xxx. */ + + if (v != '\0') { + /* Scanning fractional part */ + if ((psz[i] = szVal[j]) == '.') { dot_seen = 1; ++i; + ++j; ipf = i; - while ((v = szVal[i]) != 0) { /* get fraction part. */ + while ((v = psz[i] = szVal[j]) != '\0') { + if (!strict_p && ISSPACE(v)) { + v = psz[i] = '\0'; + break; + } + if (v == '_') { + if (nf > 0 && ISDIGIT(szVal[j+1])) { + ++j; + continue; + } + if (!strict_p) { + v = psz[i] = '\0'; + if (nf == 0) { + dot_seen = 0; + } + break; + } + goto invalid_value; + } if (!ISDIGIT(v)) break; ++i; + ++j; ++nf; } } - ipe = 0; /* Exponent */ - - switch (szVal[i]) { - case '\0': - break; - case 'e': case 'E': - case 'd': case 'D': - exp_seen = 1; - ++i; - ipe = i; - v = szVal[i]; - if ((v == '-') || (v == '+')) ++i; - while ((v=szVal[i]) != 0) { - if (!ISDIGIT(v)) break; + + /* Scanning exponential part */ + if (v != '\0') { + switch ((psz[i] = szVal[j])) { + case '\0': + break; + case 'e': case 'E': + case 'd': case 'D': + exp_seen = 1; ++i; - ++ne; - } - break; - default: - break; + ++j; + ipe = i; + v = psz[i] = szVal[j]; + if ((v == '-') || (v == '+')) { + ++i; + ++j; + } + while ((v = psz[i] = szVal[j]) != '\0') { + if (!strict_p && ISSPACE(v)) { + v = psz[i] = '\0'; + break; + } + if (v == '_') { + if (ne > 0 && ISDIGIT(szVal[j+1])) { + ++j; + continue; + } + if (!strict_p) { + v = psz[i] = '\0'; + if (ne == 0) { + exp_seen = 0; + } + break; + } + goto invalid_value; + } + if (!ISDIGIT(v)) break; + ++i; + ++j; + ++ne; + } + break; + default: + break; + } + } + + if (v != '\0') { + /* Scanning trailing spaces */ + while (ISSPACE(szVal[j])) ++j; + + /* Invalid character */ + if (szVal[j]) { + goto invalid_value; + } } } + + psz[i] = '\0'; + if (((ni == 0 || dot_seen) && nf == 0) || (exp_seen && ne == 0)) { - VALUE str = rb_str_new2(orig_szVal); - rb_raise(rb_eArgError, "invalid value for BigDecimal(): \"%"PRIsVALUE"\"", str); + VALUE str; + invalid_value: + if (!strict_p) { + goto return_zero; + } + if (!exc) { + return NULL; + } + str = rb_str_new2(orig_szVal); + rb_raise(rb_eArgError, "invalid value for BigDecimal(): \"%"PRIsVALUE"\"", str); } nalloc = (ni + nf + BASE_FIG - 1) / BASE_FIG + 1; /* set effective allocation */ @@ -4191,7 +4301,7 @@ VpAlloc(size_t mx, const char *szVal) /* xmalloc() alway returns(or throw interruption) */ vp->MaxPrec = mx; /* set max precision */ VpSetZero(vp, sign); - VpCtoV(vp, &szVal[ipn], ni, &szVal[ipf], nf, &szVal[ipe], ne); + VpCtoV(vp, psz, ni, psz + ipf, nf, psz + ipe, ne); rb_str_resize(buf, 0); return vp; } @@ -4754,7 +4864,7 @@ VpMult(Real *c, Real *a, Real *b) if (MxIndC < MxIndAB) { /* The Max. prec. of c < Prec(a)+Prec(b) */ w = c; - c = VpAlloc((size_t)((MxIndAB + 1) * BASE_FIG), "#0"); + c = VpAlloc((size_t)((MxIndAB + 1) * BASE_FIG), "#0", 1, 1); MxIndC = MxIndAB; } @@ -5922,8 +6032,8 @@ VpSqrt(Real *y, Real *x) if (x->MaxPrec > (size_t)n) n = (ssize_t)x->MaxPrec; /* allocate temporally variables */ - f = VpAlloc(y->MaxPrec * (BASE_FIG + 2), "#1"); - r = VpAlloc((n + n) * (BASE_FIG + 2), "#1"); + f = VpAlloc(y->MaxPrec * (BASE_FIG + 2), "#1", 1, 1); + r = VpAlloc((n + n) * (BASE_FIG + 2), "#1", 1, 1); nr = 0; y_prec = y->MaxPrec; @@ -6375,8 +6485,8 @@ VpPower(Real *y, Real *x, SIGNED_VALUE n) /* Allocate working variables */ - w1 = VpAlloc((y->MaxPrec + 2) * BASE_FIG, "#0"); - w2 = VpAlloc((w1->MaxPrec * 2 + 1) * BASE_FIG, "#0"); + w1 = VpAlloc((y->MaxPrec + 2) * BASE_FIG, "#0", 1, 1); + w2 = VpAlloc((w1->MaxPrec * 2 + 1) * BASE_FIG, "#0", 1, 1); /* calculation start */ VpAsgn(y, x, 1); diff --git a/ext/bigdecimal/bigdecimal.def b/ext/bigdecimal/bigdecimal.def new file mode 100644 index 0000000000..615bf72e20 --- /dev/null +++ b/ext/bigdecimal/bigdecimal.def @@ -0,0 +1,3 @@ +EXPORTS +rmpd_util_str_to_d +Init_bigdecimal diff --git a/ext/bigdecimal/bigdecimal.gemspec b/ext/bigdecimal/bigdecimal.gemspec index c8c90870ea..c65c1da0a2 100644 --- a/ext/bigdecimal/bigdecimal.gemspec +++ b/ext/bigdecimal/bigdecimal.gemspec @@ -1,6 +1,6 @@ # coding: utf-8 -bigdecimal_version = '1.3.4' +bigdecimal_version = '1.4.0.pre.20181204a' Gem::Specification.new do |s| s.name = "bigdecimal" @@ -14,13 +14,16 @@ Gem::Specification.new do |s| s.license = "ruby" s.require_paths = %w[lib] - s.extensions = %w[ext/bigdecimal/extconf.rb] + s.extensions = %w[ext/bigdecimal/extconf.rb ext/bigdecimal/util/extconf.rb] s.files = %w[ bigdecimal.gemspec ext/bigdecimal/bigdecimal.c + ext/bigdecimal/bigdecimal.def ext/bigdecimal/bigdecimal.h ext/bigdecimal/depend ext/bigdecimal/extconf.rb + ext/bigdecimal/util/extconf.rb + ext/bigdecimal/util/util.c lib/bigdecimal/jacobian.rb lib/bigdecimal/ludcmp.rb lib/bigdecimal/math.rb @@ -31,9 +34,11 @@ Gem::Specification.new do |s| sample/pi.rb ] + s.required_ruby_version = Gem::Requirement.new(">= 2.3.0".freeze) + s.add_development_dependency "rake", "~> 10.0" s.add_development_dependency "rake-compiler", ">= 0.9" s.add_development_dependency "rake-compiler-dock", ">= 0.6.1" - s.add_development_dependency "minitest", "~> 4.7.5" + s.add_development_dependency "minitest", "< 5.0.0" s.add_development_dependency "pry" end diff --git a/ext/bigdecimal/bigdecimal.h b/ext/bigdecimal/bigdecimal.h index 3297f18867..e3eae06e67 100644 --- a/ext/bigdecimal/bigdecimal.h +++ b/ext/bigdecimal/bigdecimal.h @@ -308,7 +308,7 @@ VP_EXPORT size_t VpInit(BDIGIT BaseVal); VP_EXPORT void *VpMemAlloc(size_t mb); VP_EXPORT void *VpMemRealloc(void *ptr, size_t mb); VP_EXPORT void VpFree(Real *pv); -VP_EXPORT Real *VpAlloc(size_t mx, const char *szVal); +VP_EXPORT Real *VpAlloc(size_t mx, const char *szVal, int strict_p, int exc); VP_EXPORT size_t VpAsgn(Real *c, Real *a, int isw); VP_EXPORT size_t VpAddSub(Real *c,Real *a,Real *b,int operation); VP_EXPORT size_t VpMult(Real *c,Real *a,Real *b); diff --git a/ext/bigdecimal/depend b/ext/bigdecimal/depend index 6783192b30..78833701ef 100644 --- a/ext/bigdecimal/depend +++ b/ext/bigdecimal/depend @@ -1,3 +1,5 @@ +extconf.h: $(srcdir)/$(GEMSPEC) + # AUTOGENERATED DEPENDENCIES START bigdecimal.o: $(RUBY_EXTCONF_H) bigdecimal.o: $(arch_hdrdir)/ruby/config.h diff --git a/ext/bigdecimal/extconf.rb b/ext/bigdecimal/extconf.rb index e565da891a..2ae5e1b720 100644 --- a/ext/bigdecimal/extconf.rb +++ b/ext/bigdecimal/extconf.rb @@ -17,8 +17,6 @@ bigdecimal_version = $defs << %Q[-DRUBY_BIGDECIMAL_VERSION=\\"#{bigdecimal_version}\\"] -alias __have_macro__ have_macro - have_func("labs", "stdlib.h") have_func("llabs", "stdlib.h") have_func("finite", "math.h") @@ -31,5 +29,5 @@ have_func("rb_array_const_ptr", "ruby.h") have_func("rb_sym2str", "ruby.h") create_makefile('bigdecimal') {|mf| - mf << "\nall:\n\nextconf.h: $(srcdir)/#{gemspec_name}\n" + mf << "GEMSPEC = #{gemspec_name}\n" } diff --git a/ext/bigdecimal/lib/bigdecimal/jacobian.rb b/ext/bigdecimal/lib/bigdecimal/jacobian.rb index 9cad06c09b..84c50248b7 100644 --- a/ext/bigdecimal/lib/bigdecimal/jacobian.rb +++ b/ext/bigdecimal/lib/bigdecimal/jacobian.rb @@ -21,6 +21,9 @@ # # fx is f.values(x). # + +require 'bigdecimal' + module Jacobian module_function diff --git a/ext/bigdecimal/lib/bigdecimal/util.rb b/ext/bigdecimal/lib/bigdecimal/util.rb index 911fa6fe3a..88f490cb45 100644 --- a/ext/bigdecimal/lib/bigdecimal/util.rb +++ b/ext/bigdecimal/lib/bigdecimal/util.rb @@ -5,6 +5,8 @@ # and provides BigDecimal#to_d and BigDecimal#to_digits. #++ +require 'bigdecimal' +require 'bigdecimal/util.so' class Integer < Numeric # call-seq: @@ -42,8 +44,8 @@ class Float < Numeric # # See also BigDecimal::new. # - def to_d(precision=nil) - BigDecimal(self, precision || Float::DIG) + def to_d(precision=Float::DIG) + BigDecimal(self, precision) end end @@ -64,13 +66,6 @@ class String # # See also BigDecimal::new. # - def to_d - begin - BigDecimal(self) - rescue ArgumentError - BigDecimal(0) - end - end end @@ -132,3 +127,20 @@ class Rational < Numeric BigDecimal(self, precision) end end + + +class NilClass + # call-seq: + # nil.to_d -> bigdecimal + # + # Returns nil represented as a BigDecimal. + # + # require 'bigdecimal' + # require 'bigdecimal/util' + # + # nil.to_d # => 0.0 + # + def to_d + BigDecimal(0) + end +end diff --git a/ext/bigdecimal/sample/linear.rb b/ext/bigdecimal/sample/linear.rb index 3b23269f8a..516c2473be 100644 --- a/ext/bigdecimal/sample/linear.rb +++ b/ext/bigdecimal/sample/linear.rb @@ -28,8 +28,8 @@ def rd_order(na) end na = ARGV.size -zero = BigDecimal.new("0.0") -one = BigDecimal.new("1.0") +zero = BigDecimal("0.0") +one = BigDecimal("1.0") while (n=rd_order(na))>0 a = [] @@ -37,27 +37,28 @@ while (n=rd_order(na))>0 b = [] if na <= 0 # Read data from console. - printf("\nEnter coefficient matrix element A[i,j]\n"); + printf("\nEnter coefficient matrix element A[i,j]\n") for i in 0...n do for j in 0...n do printf("A[%d,%d]? ",i,j); s = ARGF.gets - a << BigDecimal.new(s); - as << BigDecimal.new(s); + a << BigDecimal(s) + as << BigDecimal(s) end - printf("Contatant vector element b[%d] ? ",i); b << BigDecimal.new(ARGF.gets); + printf("Contatant vector element b[%d] ? ",i) + b << BigDecimal(ARGF.gets) end else # Read data from specified file. - printf("Coefficient matrix and constant vector.\n"); + printf("Coefficient matrix and constant vector.\n") for i in 0...n do s = ARGF.gets printf("%d) %s",i,s) s = s.split for j in 0...n do - a << BigDecimal.new(s[j]); - as << BigDecimal.new(s[j]); + a << BigDecimal(s[j]) + as << BigDecimal(s[j]) end - b << BigDecimal.new(s[n]); + b << BigDecimal(s[n]) end end x = lusolve(a,b,ludecomp(a,n,zero,one),zero) diff --git a/ext/bigdecimal/sample/nlsolve.rb b/ext/bigdecimal/sample/nlsolve.rb index b1dd08e0a3..c2227dac73 100644 --- a/ext/bigdecimal/sample/nlsolve.rb +++ b/ext/bigdecimal/sample/nlsolve.rb @@ -12,11 +12,11 @@ include Newton class Function # :nodoc: all def initialize() - @zero = BigDecimal.new("0.0") - @one = BigDecimal.new("1.0") - @two = BigDecimal.new("2.0") - @ten = BigDecimal.new("10.0") - @eps = BigDecimal.new("1.0e-16") + @zero = BigDecimal("0.0") + @one = BigDecimal("1.0") + @two = BigDecimal("2.0") + @ten = BigDecimal("10.0") + @eps = BigDecimal("1.0e-16") end def zero;@zero;end def one ;@one ;end diff --git a/ext/bigdecimal/util/extconf.rb b/ext/bigdecimal/util/extconf.rb new file mode 100644 index 0000000000..8750db1c52 --- /dev/null +++ b/ext/bigdecimal/util/extconf.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: false +require 'mkmf' + +checking_for(checking_message("Windows")) do + case RUBY_PLATFORM + when /cygwin|mingw/ + if defined?($extlist) + build_dir = "$(TARGET_SO_DIR)../" + else + base_dir = File.expand_path('../../../..', __FILE__) + build_dir = File.join(base_dir, "tmp", RUBY_PLATFORM, "bigdecimal", RUBY_VERSION, "") + end + $libs << " #{build_dir}bigdecimal.so" + true + when /mswin/ + $DLDFLAGS << " -libpath:.." + $libs << " bigdecimal-$(arch).lib" + true + else + false + end +end + +create_makefile('bigdecimal/util') diff --git a/ext/bigdecimal/util/util.c b/ext/bigdecimal/util/util.c new file mode 100644 index 0000000000..8d38d87852 --- /dev/null +++ b/ext/bigdecimal/util/util.c @@ -0,0 +1,9 @@ +#include "ruby.h" + +RUBY_EXTERN VALUE rmpd_util_str_to_d(VALUE str); + +void +Init_util(void) +{ + rb_define_method(rb_cString, "to_d", rmpd_util_str_to_d, 0); +} diff --git a/spec/ruby/library/bigdecimal/BigDecimal_spec.rb b/spec/ruby/library/bigdecimal/BigDecimal_spec.rb index d278512737..52f026fb2b 100644 --- a/spec/ruby/library/bigdecimal/BigDecimal_spec.rb +++ b/spec/ruby/library/bigdecimal/BigDecimal_spec.rb @@ -34,11 +34,22 @@ describe "Kernel#BigDecimal" do BigDecimal(" \t\n \r-Infinity \n").infinite?.should == -1 end - it "ignores trailing garbage" do - BigDecimal("123E45ruby").should == BigDecimal("123E45") - BigDecimal("123x45").should == BigDecimal("123") - BigDecimal("123.4%E5").should == BigDecimal("123.4") - BigDecimal("1E2E3E4E5E").should == BigDecimal("100") + ruby_version_is ""..."2.6" do + it "ignores trailing garbage" do + BigDecimal("123E45ruby").should == BigDecimal("123E45") + BigDecimal("123x45").should == BigDecimal("123") + BigDecimal("123.4%E5").should == BigDecimal("123.4") + BigDecimal("1E2E3E4E5E").should == BigDecimal("100") + end + end + + ruby_version_is "2.6" do + it "does not ignores trailing garbage" do + lambda { BigDecimal("123E45ruby") }.should raise_error(ArgumentError) + lambda { BigDecimal("123x45") }.should raise_error(ArgumentError) + lambda { BigDecimal("123.4%E5") }.should raise_error(ArgumentError) + lambda { BigDecimal("1E2E3E4E5E") }.should raise_error(ArgumentError) + end end ruby_version_is ""..."2.4" do @@ -59,12 +70,24 @@ describe "Kernel#BigDecimal" do BigDecimal(".123").should == BigDecimal("0.123") end - it "allows for underscores in all parts" do - reference = BigDecimal("12345.67E89") + ruby_version_is ""..."2.6" do + it "allows for underscores in all parts" do + reference = BigDecimal("12345.67E89") - BigDecimal("12_345.67E89").should == reference - BigDecimal("1_2_3_4_5_._6____7_E89").should == reference - BigDecimal("12345_.67E_8__9_").should == reference + BigDecimal("12_345.67E89").should == reference + BigDecimal("1_2_3_4_5_._6____7_E89").should == reference + BigDecimal("12345_.67E_8__9_").should == reference + end + end + + ruby_version_is "2.6" do + it "process underscores as Float()" do + reference = BigDecimal("12345.67E89") + + BigDecimal("12_345.67E89").should == reference + lambda { BigDecimal("1_2_3_4_5_._6____7_E89") }.should raise_error(ArgumentError) + lambda { BigDecimal("12345_.67E_8__9_") }.should raise_error(ArgumentError) + end end it "accepts NaN and [+-]Infinity" do diff --git a/spec/ruby/library/bigdecimal/ver_spec.rb b/spec/ruby/library/bigdecimal/ver_spec.rb deleted file mode 100644 index 15c7099306..0000000000 --- a/spec/ruby/library/bigdecimal/ver_spec.rb +++ /dev/null @@ -1,11 +0,0 @@ -require_relative '../../spec_helper' -require 'bigdecimal' - -describe "BigDecimal.ver" do - - it "returns the Version number" do - lambda {BigDecimal.ver }.should_not raise_error() - BigDecimal.ver.should_not == nil - end - -end diff --git a/test/bigdecimal/test_bigdecimal.rb b/test/bigdecimal/test_bigdecimal.rb index bb9ec92481..ff295b4270 100644 --- a/test/bigdecimal/test_bigdecimal.rb +++ b/test/bigdecimal/test_bigdecimal.rb @@ -44,49 +44,71 @@ class TestBigDecimal < Test::Unit::TestCase end def test_not_equal - assert_not_equal BigDecimal("1"), BigDecimal.allocate + assert_not_equal BigDecimal("1"), BigDecimal("2") end - def test_global_new + def test_BigDecimal assert_equal(1, BigDecimal("1")) assert_equal(1, BigDecimal("1", 1)) + assert_equal(1, BigDecimal(" 1 ")) + assert_equal(111, BigDecimal("1_1_1_")) + assert_equal(10**(-1), BigDecimal("1E-1"), '#4825') + assert_equal(1234, BigDecimal(" \t\n\r \r1234 \t\n\r \r")) + assert_raise(ArgumentError) { BigDecimal("1", -1) } + assert_raise(ArgumentError, /"1__1_1"/) { BigDecimal("1__1_1") } + assert_raise(ArgumentError, /"_1_1_1"/) { BigDecimal("_1_1_1") } BigDecimal.save_exception_mode do BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false) BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false) - assert_equal(1234, BigDecimal(" \t\n\r \r1234 \t\n\r \r")) + assert_positive_infinite(BigDecimal("Infinity")) + assert_positive_infinite(BigDecimal("1E1111111111111111111")) assert_positive_infinite(BigDecimal(" \t\n\r \rInfinity \t\n\r \r")) + assert_negative_infinite(BigDecimal("-Infinity")) assert_negative_infinite(BigDecimal(" \t\n\r \r-Infinity \t\n\r \r")) + assert_nan(BigDecimal("NaN")) assert_nan(BigDecimal(" \t\n\r \rNaN \t\n\r \r")) end end - def test_global_new_with_invalid_string + def test_BigDecimal_with_invalid_string [ '', '.', 'e1', 'd1', '.e', '.d', '1.e', '1.d', '.1e', '.1d', - 'invlaid value' + '2,30', '19,000.0', '-2,30', '-19,000.0', '+2,30', '+19,000.0', + '2.3,0', '19.000,0', '-2.3,0', '-19.000,0', '+2.3,0', '+19.000,0', + '2.3.0', '19.000.0', '-2.3.0', '-19.000.0', '+2.3.0', '+19.000.0', + 'invlaid value', '123 xyz' ].each do |invalid_string| assert_raise_with_message(ArgumentError, %Q[invalid value for BigDecimal(): "#{invalid_string}"]) do BigDecimal(invalid_string) end end + + BigDecimal.save_exception_mode do + BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false) + BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false) + assert_raise(ArgumentError, /"Infinity_"/) { BigDecimal("Infinity_") } + assert_raise(ArgumentError, /"+Infinity_"/) { BigDecimal("+Infinity_") } + assert_raise(ArgumentError, /"-Infinity_"/) { BigDecimal("-Infinity_") } + assert_raise(ArgumentError, /"NaN_"/) { BigDecimal("NaN_") } + end end - def test_global_new_with_integer + def test_BigDecimal_with_integer assert_equal(BigDecimal("1"), BigDecimal(1)) assert_equal(BigDecimal("-1"), BigDecimal(-1)) assert_equal(BigDecimal((2**100).to_s), BigDecimal(2**100)) assert_equal(BigDecimal((-2**100).to_s), BigDecimal(-2**100)) end - def test_global_new_with_rational + def test_BigDecimal_with_rational assert_equal(BigDecimal("0.333333333333333333333"), BigDecimal(1.quo(3), 21)) assert_equal(BigDecimal("-0.333333333333333333333"), BigDecimal(-1.quo(3), 21)) assert_raise_with_message(ArgumentError, "can't omit precision for a Rational.") { BigDecimal(42.quo(7)) } end - def test_global_new_with_float + def test_BigDecimal_with_float assert_equal(BigDecimal("0.1235"), BigDecimal(0.1234567, 4)) assert_equal(BigDecimal("-0.1235"), BigDecimal(-0.1234567, 4)) assert_raise_with_message(ArgumentError, "can't omit precision for a Float.") { BigDecimal(4.2) } @@ -107,7 +129,7 @@ class TestBigDecimal < Test::Unit::TestCase end end - def test_global_new_with_big_decimal + def test_BigDecimal_with_big_decimal assert_equal(BigDecimal(1), BigDecimal(BigDecimal(1))) assert_equal(BigDecimal('+0'), BigDecimal(BigDecimal('+0'))) assert_equal(BigDecimal('-0'), BigDecimal(BigDecimal('-0'))) @@ -120,7 +142,7 @@ class TestBigDecimal < Test::Unit::TestCase end end - def test_global_new_with_tainted_string + def test_BigDecimal_with_tainted_string Thread.new { $SAFE = 1 BigDecimal('1'.taint) @@ -129,76 +151,73 @@ class TestBigDecimal < Test::Unit::TestCase $SAFE = 0 end - def test_s_ver - assert_warning(/BigDecimal\.ver is deprecated; use BigDecimal::VERSION instead/) do - BigDecimal.ver - end - end - - def test_s_new - assert_warning(/BigDecimal.new is deprecated/) do - BigDecimal.new("1") - end - end - - def test_new - assert_equal(1, BigDecimal("1")) - assert_equal(1, BigDecimal("1", 1)) - assert_equal(1, BigDecimal(" 1 ")) - assert_equal(111, BigDecimal("1_1_1_")) - assert_equal(10**(-1), BigDecimal("1E-1"), '#4825') - - assert_raise(ArgumentError, /"_1_1_1"/) { BigDecimal("_1_1_1") } - - BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false) - BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false) - assert_positive_infinite(BigDecimal("Infinity")) - assert_negative_infinite(BigDecimal("-Infinity")) - assert_nan(BigDecimal("NaN")) - assert_positive_infinite(BigDecimal("1E1111111111111111111")) - end - - def test_new_with_integer - assert_equal(BigDecimal("1"), BigDecimal(1)) - assert_equal(BigDecimal("-1"), BigDecimal(-1)) - assert_equal(BigDecimal((2**100).to_s), BigDecimal(2**100)) - assert_equal(BigDecimal((-2**100).to_s), BigDecimal(-2**100)) - end - - def test_new_with_rational - assert_equal(BigDecimal("0.333333333333333333333"), BigDecimal(1.quo(3), 21)) - assert_equal(BigDecimal("-0.333333333333333333333"), BigDecimal(-1.quo(3), 21)) - assert_raise(ArgumentError) { BigDecimal(1.quo(3)) } + def test_BigDecimal_with_exception_keyword + assert_raise(ArgumentError) { + BigDecimal('.', exception: true) + } + assert_nothing_raised(ArgumentError) { + assert_equal(nil, BigDecimal(".", exception: false)) + } + assert_raise(ArgumentError) { + BigDecimal("1", -1, exception: true) + } + assert_nothing_raised(ArgumentError) { + assert_equal(nil, BigDecimal("1", -1, exception: false)) + } + assert_raise(ArgumentError) { + BigDecimal(42.quo(7), exception: true) + } + assert_nothing_raised(ArgumentError) { + assert_equal(nil, BigDecimal(42.quo(7), exception: false)) + } + assert_raise(ArgumentError) { + BigDecimal(4.2, exception: true) + } + assert_nothing_raised(ArgumentError) { + assert_equal(nil, BigDecimal(4.2, exception: false)) + } + # TODO: support conversion from complex + # assert_raise(RangeError) { + # BigDecimal(1i, exception: true) + # } + # assert_nothing_raised(RangeError) { + # assert_equal(nil, BigDecimal(1i, exception: false)) + # } + assert_raise(TypeError) { + BigDecimal(nil, exception: true) + } + assert_nothing_raised(TypeError) { + assert_equal(nil, BigDecimal(nil, exception: false)) + } + assert_nothing_raised(TypeError) { + assert_equal(nil, BigDecimal(:test, exception: false)) + } + assert_nothing_raised(TypeError) { + assert_equal(nil, BigDecimal(Object.new, exception: false)) + } + # TODO: support to_d + # assert_nothing_raised(TypeError) { + # o = Object.new + # def o.to_d; 3.14; end + # assert_equal(3.14, BigDecimal(o, exception: false)) + # } + # assert_nothing_raised(RuntimeError) { + # o = Object.new + # def o.to_d; raise; end + # assert_equal(nil, BigDecimal(o, exception: false)) + # } end - def test_new_with_float - assert_equal(BigDecimal("0.1235"), BigDecimal(0.1234567, 4)) - assert_equal(BigDecimal("-0.1235"), BigDecimal(-0.1234567, 4)) - assert_raise(ArgumentError) { BigDecimal(0.1) } - assert_raise(ArgumentError) { BigDecimal(0.1, Float::DIG + 2) } - assert_nothing_raised { BigDecimal(0.1, Float::DIG + 1) } + def test_s_ver + assert_raise(NoMethodError, /undefined method `ver`/) { BigDecimal.ver } end - def test_new_with_big_decimal - assert_equal(BigDecimal(1), BigDecimal(BigDecimal(1))) - assert_equal(BigDecimal('+0'), BigDecimal(BigDecimal('+0'))) - assert_equal(BigDecimal('-0'), BigDecimal(BigDecimal('-0'))) - BigDecimal.save_exception_mode do - BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false) - BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false) - assert_positive_infinite(BigDecimal(BigDecimal('Infinity'))) - assert_negative_infinite(BigDecimal(BigDecimal('-Infinity'))) - assert_nan(BigDecimal(BigDecimal('NaN'))) - end + def test_s_allocate + assert_raise(NoMethodError, /undefined method `allocate`/) { BigDecimal.allocate } end - def test_new_with_tainted_string - Thread.new { - $SAFE = 1 - BigDecimal('1'.taint) - }.join - ensure - $SAFE = 0 + def test_s_new + assert_raise(NoMethodError, /undefined method `new`/) { BigDecimal.new("1") } end def _test_mode(type) @@ -1778,6 +1797,12 @@ class TestBigDecimal < Test::Unit::TestCase EOS end + def test_frozen_p + x = BigDecimal(1) + assert(x.frozen?) + assert((x + x).frozen?) + end + def test_clone assert_warning(/^$/) do x = BigDecimal(0) @@ -1795,14 +1820,8 @@ class TestBigDecimal < Test::Unit::TestCase end def test_dup_subclass - assert_warning(/BigDecimal\.new is deprecated/) do - c = Class.new(BigDecimal) - x = c.new(1) - y = x.dup - assert_same(x, y) - assert_equal(1, y) - assert_kind_of(c, y) - end + c = Class.new(BigDecimal) + assert_raise(NoMethodError, /undefined method `new`/) { c.new(1) } end def test_to_d @@ -1834,7 +1853,7 @@ class TestBigDecimal < Test::Unit::TestCase assert_no_memory_leak("BigDecimal()") end - def test_no_memory_leak_global_new + def test_no_memory_leak_BigDecimal assert_no_memory_leak("BigDecimal('10')") assert_no_memory_leak("BigDecimal(b)") end diff --git a/test/bigdecimal/test_bigdecimal_util.rb b/test/bigdecimal/test_bigdecimal_util.rb index fd457f6bf5..04c8eb2b46 100644 --- a/test/bigdecimal/test_bigdecimal_util.rb +++ b/test/bigdecimal/test_bigdecimal_util.rb @@ -12,6 +12,8 @@ class TestBigDecimalUtil < Test::Unit::TestCase def test_Integer_to_d assert_equal(BigDecimal(1), 1.to_d) assert_equal(BigDecimal(2<<100), (2<<100).to_d) + + assert(1.to_d.frozen?) end def test_Float_to_d_without_precision @@ -22,6 +24,11 @@ class TestBigDecimalUtil < Test::Unit::TestCase bug9214 = '[ruby-core:58858]' assert_equal((-0.0).to_d.sign, -1, bug9214) + + assert_raise(TypeError) { 0.3.to_d(nil) } + assert_raise(TypeError) { 0.3.to_d(false) } + + assert(1.1.to_d.frozen?) end def test_Float_to_d_with_precision @@ -32,6 +39,8 @@ class TestBigDecimalUtil < Test::Unit::TestCase bug9214 = '[ruby-core:58858]' assert_equal((-0.0).to_d(digits).sign, -1, bug9214) + + assert(1.1.to_d(digits).frozen?) end def test_Rational_to_d @@ -39,6 +48,8 @@ class TestBigDecimalUtil < Test::Unit::TestCase delta = 1.0/10**(digits) assert_in_delta(BigDecimal(1.quo(2), digits), 1.quo(2).to_d(digits), delta) assert_in_delta(BigDecimal(355.quo(113), digits), 355.quo(113).to_d(digits), delta) + + assert(355.quo(113).to_d(digits).frozen?) end def test_Rational_to_d_with_zero_precision @@ -50,11 +61,30 @@ class TestBigDecimalUtil < Test::Unit::TestCase end def test_String_to_d - assert_equal("2.5".to_d, BigDecimal('2.5')) + assert_equal(BigDecimal('1'), "1__1_1".to_d) + assert_equal(BigDecimal('2.5'), "2.5".to_d) + assert_equal(BigDecimal('2.5'), "2.5 degrees".to_d) + assert_equal(BigDecimal('2.5e1'), "2.5e1 degrees".to_d) + assert_equal(BigDecimal('0'), "degrees 100.0".to_d) + assert_equal(BigDecimal('0.125'), "0.1_2_5".to_d) + assert_equal(BigDecimal('0.125'), "0.1_2_5__".to_d) + assert_equal(BigDecimal('1'), "1_.125".to_d) + assert_equal(BigDecimal('1'), "1._125".to_d) + assert_equal(BigDecimal('0.1'), "0.1__2_5".to_d) + assert_equal(BigDecimal('0.1'), "0.1_e10".to_d) + assert_equal(BigDecimal('0.1'), "0.1e_10".to_d) + assert_equal(BigDecimal('1'), "0.1e1__0".to_d) + + assert("2.5".to_d.frozen?) end def test_invalid_String_to_d assert_equal("invalid".to_d, BigDecimal('0.0')) end + def test_Nil_to_d + assert_equal(nil.to_d, BigDecimal('0.0')) + + assert(nil.to_d) + end end -- cgit v1.2.3