diff --git a/ext/bigdecimal/bigdecimal.c b/ext/bigdecimal/bigdecimal.c index 7df56e3aa1..dd8f0f9d85 100644 --- a/ext/bigdecimal/bigdecimal.c +++ b/ext/bigdecimal/bigdecimal.c @@ -62,6 +62,7 @@ static ID id_ceil; static ID id_floor; static ID id_to_r; static ID id_eq; +static ID id_half; /* MACRO's to guard objects from GC by keeping them in stack */ #define ENTER(n) volatile VALUE RB_UNUSED_VAR(vStack[n]);int iStack=0 @@ -126,6 +127,9 @@ rb_rational_den(VALUE rat) } #endif +#define BIGDECIMAL_POSITIVE_P(bd) ((bd)->sign > 0) +#define BIGDECIMAL_NEGATIVE_P(bd) ((bd)->sign < 0) + /* * ================== Ruby Interface part ========================== */ @@ -240,9 +244,8 @@ again: if (prec > DBL_DIG+1) goto SomeOneMayDoIt; d = RFLOAT_VALUE(v); if (!isfinite(d)) { - pv = VpCreateRbObject(prec, NULL); - pv->sign = isnan(d) ? VP_SIGN_NaN : - d > 0 ? VP_SIGN_POSITIVE_INFINITE : VP_SIGN_NEGATIVE_FINITE; + pv = VpCreateRbObject(1, NULL); + VpDtoV(pv, d); return pv; } if (d != 0.0) { @@ -385,9 +388,9 @@ BigDecimal_hash(VALUE self) * Method used to provide marshalling support. * * inf = BigDecimal.new('Infinity') - * #=> # + * #=> Infinity * BigDecimal._load(inf._dump) - * #=> # + * #=> Infinity * * See the Marshal module. */ @@ -440,6 +443,54 @@ BigDecimal_load(VALUE self, VALUE str) return ToValue(pv); } +static unsigned short +check_rounding_mode_option(VALUE const opts) +{ + VALUE mode; + char const *s; + long l; + + assert(RB_TYPE_P(opts, T_HASH)); + + if (NIL_P(opts)) + goto noopt; + + mode = rb_hash_lookup2(opts, ID2SYM(id_half), Qundef); + if (mode == Qundef) + goto noopt; + + if (SYMBOL_P(mode)) + mode = rb_sym2str(mode); + else if (!RB_TYPE_P(mode, T_STRING)) { + VALUE str_mode = rb_check_string_type(mode); + if (NIL_P(str_mode)) goto invalid; + mode = str_mode; + } + s = RSTRING_PTR(mode); + l = RSTRING_LEN(mode); + switch (l) { + case 2: + if (strncasecmp(s, "up", 2) == 0) + return VP_ROUND_HALF_UP; + break; + case 4: + if (strncasecmp(s, "even", 4) == 0) + return VP_ROUND_HALF_EVEN; + else if (strncasecmp(s, "down", 4) == 0) + return VP_ROUND_HALF_DOWN; + default: + break; + } + invalid: + if (NIL_P(mode)) + rb_raise(rb_eArgError, "invalid rounding mode: nil"); + else + rb_raise(rb_eArgError, "invalid rounding mode: %"PRIsVALUE, mode); + + noopt: + return VpGetRoundMode(); +} + static unsigned short check_rounding_mode(VALUE const v) { @@ -562,7 +613,7 @@ BigDecimal_mode(int argc, VALUE *argv, VALUE self) fo = VpSetRoundMode(sw); return INT2FIX(fo); } - rb_raise(rb_eTypeError, "first argument for BigDecimal#mode invalid"); + rb_raise(rb_eTypeError, "first argument for BigDecimal.mode invalid"); return Qnil; } @@ -682,7 +733,7 @@ static VALUE BigDecimal_split(VALUE self); /* Returns the value as an Integer. * - * If the BigNumber is infinity or NaN, raises FloatDomainError. + * If the BigDecimal is infinity or NaN, raises FloatDomainError. */ static VALUE BigDecimal_to_i(VALUE self) @@ -707,7 +758,7 @@ BigDecimal_to_i(VALUE self) VALUE ret; ssize_t dpower = e - (ssize_t)RSTRING_LEN(digits); - if (VpGetSign(p) < 0) { + if (BIGDECIMAL_NEGATIVE_P(p)) { numerator = rb_funcall(numerator, '*', 1, INT2FIX(-1)); } if (dpower < 0) { @@ -762,17 +813,17 @@ BigDecimal_to_f(VALUE self) overflow: VpException(VP_EXCEPTION_OVERFLOW, "BigDecimal to Float conversion", 0); - if (p->sign >= 0) - return rb_float_new(VpGetDoublePosInf()); - else + if (BIGDECIMAL_NEGATIVE_P(p)) return rb_float_new(VpGetDoubleNegInf()); + else + return rb_float_new(VpGetDoublePosInf()); underflow: VpException(VP_EXCEPTION_UNDERFLOW, "BigDecimal to Float conversion", 0); - if (p->sign >= 0) - return rb_float_new(0.0); - else + if (BIGDECIMAL_NEGATIVE_P(p)) return rb_float_new(-0.0); + else + return rb_float_new(0.0); } @@ -1720,11 +1771,21 @@ BigDecimal_round(int argc, VALUE *argv, VALUE self) iLoc = 0; break; case 1: - iLoc = NUM2INT(vLoc); + if (RB_TYPE_P(vLoc, T_HASH)) { + sw = check_rounding_mode_option(vLoc); + } + else { + iLoc = NUM2INT(vLoc); + } break; case 2: iLoc = NUM2INT(vLoc); - sw = check_rounding_mode(vRound); + if (RB_TYPE_P(vRound, T_HASH)) { + sw = check_rounding_mode_option(vRound); + } + else { + sw = check_rounding_mode(vRound); + } break; default: break; @@ -2066,7 +2127,7 @@ BigDecimal_exponent(VALUE self) * values in angle brackets with a leading #: * * BigDecimal.new("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 @@ -2077,23 +2138,16 @@ BigDecimal_inspect(VALUE self) { ENTER(5); Real *vp; - volatile VALUE obj; + volatile VALUE str; size_t nc; - char *psz, *tmp; GUARD_OBJ(vp, GetVpValue(self, 1)); nc = VpNumOfChars(vp, "E"); - nc += (nc + 9) / 10; - obj = rb_str_new(0, nc+256); - psz = RSTRING_PTR(obj); - sprintf(psz, "#", VpPrec(vp)*VpBaseFig(), VpMaxPrec(vp)*VpBaseFig()); - rb_str_resize(obj, strlen(psz)); - return obj; + str = rb_str_new(0, nc); + VpToString(vp, RSTRING_PTR(str), 0, 0); + rb_str_resize(str, strlen(RSTRING_PTR(str))); + return str; } static VALUE BigMath_s_exp(VALUE, VALUE, VALUE); @@ -2303,7 +2357,7 @@ BigDecimal_power(int argc, VALUE*argv, VALUE self) if (is_negative(vexp)) { y = VpCreateRbObject(n, "#0"); RB_GC_GUARD(y->obj); - if (VpGetSign(x) < 0) { + if (BIGDECIMAL_NEGATIVE_P(x)) { if (is_integer(vexp)) { if (is_even(vexp)) { /* (-0) ** (-even_integer) -> Infinity */ @@ -2342,7 +2396,7 @@ BigDecimal_power(int argc, VALUE*argv, VALUE self) if (VpIsInf(x)) { if (is_negative(vexp)) { - if (VpGetSign(x) < 0) { + if (BIGDECIMAL_NEGATIVE_P(x)) { if (is_integer(vexp)) { if (is_even(vexp)) { /* (-Infinity) ** (-even_integer) -> +0 */ @@ -2364,7 +2418,7 @@ BigDecimal_power(int argc, VALUE*argv, VALUE self) } else { y = VpCreateRbObject(n, "0#"); - if (VpGetSign(x) < 0) { + if (BIGDECIMAL_NEGATIVE_P(x)) { if (is_integer(vexp)) { if (is_even(vexp)) { VpSetPosInf(y); @@ -2405,7 +2459,7 @@ BigDecimal_power(int argc, VALUE*argv, VALUE self) } return ToValue(y); } - else if (VpGetSign(x) < 0 && is_even(vexp)) { + else if (BIGDECIMAL_NEGATIVE_P(x) && is_even(vexp)) { return ToValue(VpCreateRbObject(n, "-0")); } else { @@ -2423,7 +2477,7 @@ BigDecimal_power(int argc, VALUE*argv, VALUE self) } return ToValue(y); } - else if (VpGetSign(x) < 0 && is_even(vexp)) { + else if (BIGDECIMAL_NEGATIVE_P(x) && is_even(vexp)) { return ToValue(VpCreateRbObject(n, "-0")); } else { @@ -2545,6 +2599,7 @@ BigDecimal_new(int argc, VALUE *argv) size_t mf; VALUE nFig; VALUE iniValue; + double d; if (rb_scan_args(argc, argv, "11", &iniValue, &nFig) == 1) { mf = 0; @@ -2566,6 +2621,12 @@ BigDecimal_new(int argc, VALUE *argv) return GetVpValue(iniValue, 1); case T_FLOAT: + d = RFLOAT_VALUE(iniValue); + if (!isfinite(d)) { + Real *pv = VpCreateRbObject(1, NULL); + VpDtoV(pv, d); + return pv; + } if (mf > DBL_DIG+1) { rb_raise(rb_eArgError, "precision too large."); } @@ -2766,7 +2827,7 @@ BigMath_s_exp(VALUE klass, VALUE x, VALUE vprec) case T_DATA: if (!is_kind_of_BigDecimal(x)) break; vx = DATA_PTR(x); - negative = VpGetSign(vx) < 0; + negative = BIGDECIMAL_NEGATIVE_P(vx); infinite = VpIsPosInf(vx) || VpIsNegInf(vx); nan = VpIsNaN(vx); break; @@ -2819,7 +2880,7 @@ BigMath_s_exp(VALUE klass, VALUE x, VALUE vprec) x = vx->obj; n = prec + rmpd_double_figures(); - negative = VpGetSign(vx) < 0; + negative = BIGDECIMAL_NEGATIVE_P(vx); if (negative) { VpSetSign(vx, 1); } @@ -2905,7 +2966,7 @@ BigMath_s_log(VALUE klass, VALUE x, VALUE vprec) if (!is_kind_of_BigDecimal(x)) break; vx = DATA_PTR(x); zero = VpIsZero(vx); - negative = VpGetSign(vx) < 0; + negative = BIGDECIMAL_NEGATIVE_P(vx); infinite = VpIsPosInf(vx) || VpIsNegInf(vx); nan = VpIsNaN(vx); break; @@ -3208,7 +3269,7 @@ Init_bigdecimal(void) rb_define_const(rb_cBigDecimal, "EXCEPTION_OVERFLOW", INT2FIX(VP_EXCEPTION_OVERFLOW)); /* - * 0x01: Determines what happens when a division by zero is performed. + * 0x10: Determines what happens when a division by zero is performed. * See BigDecimal.mode. */ rb_define_const(rb_cBigDecimal, "EXCEPTION_ZERODIVIDE", INT2FIX(VP_EXCEPTION_ZERODIVIDE)); @@ -3350,6 +3411,7 @@ Init_bigdecimal(void) id_floor = rb_intern_const("floor"); id_to_r = rb_intern_const("to_r"); id_eq = rb_intern_const("=="); + id_half = rb_intern_const("half"); } /* @@ -3377,7 +3439,14 @@ static Real *VpPt5; /* constant 0.5 */ #define MemCmp(x,y,z) memcmp(x,y,z) #define StrCmp(x,y) strcmp(x,y) -static int VpIsDefOP(Real *c,Real *a,Real *b,int sw); +enum op_sw { + OP_SW_ADD = 1, /* + */ + OP_SW_SUB, /* - */ + OP_SW_MULT, /* * */ + OP_SW_DIV /* / */ +}; + +static int VpIsDefOP(Real *c, Real *a, Real *b, enum op_sw sw); static int AddExponent(Real *a, SIGNED_VALUE n); static BDIGIT VpAddAbs(Real *a,Real *b,Real *c); static BDIGIT VpSubAbs(Real *a,Real *b,Real *c); @@ -3404,7 +3473,7 @@ VpMemAlloc(size_t mb) return p; } - VP_EXPORT void * +VP_EXPORT void * VpMemRealloc(void *ptr, size_t mb) { void *p = xrealloc(ptr, mb); @@ -3664,7 +3733,7 @@ VpException(unsigned short f, const char *str,int always) /* Throw exception or returns 0,when resulting c is Inf or NaN */ /* sw=1:+ 2:- 3:* 4:/ */ static int -VpIsDefOP(Real *c,Real *a,Real *b,int sw) +VpIsDefOP(Real *c, Real *a, Real *b, enum op_sw sw) { if (VpIsNaN(a) || VpIsNaN(b)) { /* at least a or b is NaN */ @@ -3675,7 +3744,7 @@ VpIsDefOP(Real *c,Real *a,Real *b,int sw) if (VpIsInf(a)) { if (VpIsInf(b)) { switch(sw) { - case 1: /* + */ + case OP_SW_ADD: /* + */ if (VpGetSign(a) == VpGetSign(b)) { VpSetInf(c, VpGetSign(a)); goto Inf; @@ -3684,7 +3753,7 @@ VpIsDefOP(Real *c,Real *a,Real *b,int sw) VpSetNaN(c); goto NaN; } - case 2: /* - */ + case OP_SW_SUB: /* - */ if (VpGetSign(a) != VpGetSign(b)) { VpSetInf(c, VpGetSign(a)); goto Inf; @@ -3693,12 +3762,10 @@ VpIsDefOP(Real *c,Real *a,Real *b,int sw) VpSetNaN(c); goto NaN; } - break; - case 3: /* * */ + case OP_SW_MULT: /* * */ VpSetInf(c, VpGetSign(a)*VpGetSign(b)); goto Inf; - break; - case 4: /* / */ + case OP_SW_DIV: /* / */ VpSetNaN(c); goto NaN; } @@ -3707,18 +3774,18 @@ VpIsDefOP(Real *c,Real *a,Real *b,int sw) } /* Inf op Finite */ switch(sw) { - case 1: /* + */ - case 2: /* - */ + case OP_SW_ADD: /* + */ + case OP_SW_SUB: /* - */ VpSetInf(c, VpGetSign(a)); break; - case 3: /* * */ + case OP_SW_MULT: /* * */ if (VpIsZero(b)) { VpSetNaN(c); goto NaN; } VpSetInf(c, VpGetSign(a)*VpGetSign(b)); break; - case 4: /* / */ + case OP_SW_DIV: /* / */ VpSetInf(c, VpGetSign(a)*VpGetSign(b)); } goto Inf; @@ -3726,20 +3793,20 @@ VpIsDefOP(Real *c,Real *a,Real *b,int sw) if (VpIsInf(b)) { switch(sw) { - case 1: /* + */ + case OP_SW_ADD: /* + */ VpSetInf(c, VpGetSign(b)); break; - case 2: /* - */ + case OP_SW_SUB: /* - */ VpSetInf(c, -VpGetSign(b)); break; - case 3: /* * */ + case OP_SW_MULT: /* * */ if (VpIsZero(a)) { VpSetNaN(c); goto NaN; } VpSetInf(c, VpGetSign(a)*VpGetSign(b)); break; - case 4: /* / */ + case OP_SW_DIV: /* / */ VpSetZero(c, VpGetSign(a)*VpGetSign(b)); } goto Inf; @@ -3747,7 +3814,13 @@ VpIsDefOP(Real *c,Real *a,Real *b,int sw) return 1; /* Results OK */ Inf: - return VpException(VP_EXCEPTION_INFINITY, "Computation results to 'Infinity'", 0); + if (VpIsPosInf(c)) { + return VpException(VP_EXCEPTION_INFINITY, "Computation results to 'Infinity'", 0); + } + else { + return VpException(VP_EXCEPTION_INFINITY, "Computation results to '-Infinity'", 0); + } + NaN: return VpException(VP_EXCEPTION_NaN, "Computation results to 'NaN'", 0); } @@ -3894,7 +3967,8 @@ overflow: VP_EXPORT Real * VpAlloc(size_t mx, const char *szVal) { - size_t i, ni, ipn, ipf, nf, ipe, ne, nalloc; + const char *orig_szVal = szVal; + size_t i, ni, ipn, ipf, nf, ipe, ne, dot_seen, exp_seen, nalloc; char v, *psz; int sign=1; Real *vp = NULL; @@ -3929,6 +4003,26 @@ VpAlloc(size_t mx, const char *szVal) return vp; } + /* 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; + } + /* Skip all '_' after digit: 2006-6-30 */ ni = 0; buf = rb_str_tmp_new(strlen(szVal) + 1); @@ -3954,26 +4048,6 @@ VpAlloc(size_t mx, const char *szVal) } 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; - } - /* check on number szVal[] */ ipn = i = 0; if (szVal[i] == '-') { sign=-1; ++i; } @@ -3989,9 +4063,12 @@ VpAlloc(size_t mx, const char *szVal) ipf = 0; ipe = 0; ne = 0; + dot_seen = 0; + exp_seen = 0; if (v) { /* other than digit nor \0 */ if (szVal[i] == '.') { /* xxx. */ + dot_seen = 1; ++i; ipf = i; while ((v = szVal[i]) != 0) { /* get fraction part. */ @@ -4007,6 +4084,7 @@ VpAlloc(size_t mx, const char *szVal) break; case 'e': case 'E': case 'd': case 'D': + exp_seen = 1; ++i; ipe = i; v = szVal[i]; @@ -4021,6 +4099,11 @@ VpAlloc(size_t mx, const char *szVal) break; } } + 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); + } + nalloc = (ni + nf + BASE_FIG - 1) / BASE_FIG + 1; /* set effective allocation */ /* units for szVal[] */ if (mx == 0) mx = 1; @@ -4107,7 +4190,7 @@ VpAddSub(Real *c, Real *a, Real *b, int operation) } #endif /* BIGDECIMAL_DEBUG */ - if (!VpIsDefOP(c, a, b, (operation > 0) ? 1 : 2)) return 0; /* No significant digits */ + if (!VpIsDefOP(c, a, b, (operation > 0) ? OP_SW_ADD : OP_SW_SUB)) return 0; /* No significant digits */ /* check if a or b is zero */ if (VpIsZero(a)) { @@ -4563,7 +4646,7 @@ VpMult(Real *c, Real *a, Real *b) } #endif /* BIGDECIMAL_DEBUG */ - if (!VpIsDefOP(c, a, b, 3)) return 0; /* No significant digit */ + if (!VpIsDefOP(c, a, b, OP_SW_MULT)) return 0; /* No significant digit */ if (VpIsZero(a) || VpIsZero(b)) { /* at least a or b is zero */ @@ -4675,7 +4758,7 @@ Exit: /* * c = a / b, remainder = r */ - VP_EXPORT size_t +VP_EXPORT size_t VpDivd(Real *c, Real *r, Real *a, Real *b) { size_t word_a, word_b, word_c, word_r; @@ -4693,14 +4776,14 @@ VpDivd(Real *c, Real *r, Real *a, Real *b) #endif /*BIGDECIMAL_DEBUG */ VpSetNaN(r); - if (!VpIsDefOP(c, a, b, 4)) goto Exit; + if (!VpIsDefOP(c, a, b, OP_SW_DIV)) goto Exit; if (VpIsZero(a) && VpIsZero(b)) { VpSetNaN(c); - return VpException(VP_EXCEPTION_NaN, "(VpDivd) 0/0 not defined(NaN)", 0); + return VpException(VP_EXCEPTION_NaN, "Computation results to 'NaN'", 0); } if (VpIsZero(b)) { VpSetInf(c, VpGetSign(a) * VpGetSign(b)); - return VpException(VP_EXCEPTION_ZERODIVIDE, "(VpDivd) Divide by zero", 0); + return VpException(VP_EXCEPTION_ZERODIVIDE, "Divide by zero", 0); } if (VpIsZero(a)) { /* numerator a is zero */ @@ -5053,7 +5136,7 @@ VPrint(FILE *fp, const char *cntl_chr, Real *a) nc += 9; } else if (!VpIsZero(a)) { - if (VpGetSign(a) < 0) { + if (BIGDECIMAL_NEGATIVE_P(a)) { fprintf(fp, "-"); ++nc; } @@ -5142,7 +5225,7 @@ VpFormatSt(char *psz, size_t fFmt) if (!ch) break; if (ISSPACE(ch) || ch=='-' || ch=='+') continue; if (ch == '.') { nf = 0; continue; } - if (ch == 'E') break; + if (ch == 'E' || ch == 'e') break; if (++nf > fFmt) { memmove(psz + i + 1, psz + i, ie - i + 1); @@ -5191,7 +5274,7 @@ VpSzMantissa(Real *a,char *psz) ZeroSup = 1; /* Flag not to print the leading zeros as 0.00xxxxEnn */ if (!VpIsZero(a)) { - if (VpGetSign(a) < 0) *psz++ = '-'; + if (BIGDECIMAL_NEGATIVE_P(a)) *psz++ = '-'; n = a->Prec; for (i = 0; i < n; ++i) { m = BASE1; @@ -5265,7 +5348,7 @@ VpToString(Real *a, char *psz, size_t fFmt, int fPlus) ZeroSup = 1; /* Flag not to print the leading zeros as 0.00xxxxEnn */ - if (VpGetSign(a) < 0) *psz++ = '-'; + if (BIGDECIMAL_NEGATIVE_P(a)) *psz++ = '-'; else if (fPlus == 1) *psz++ = ' '; else if (fPlus == 2) *psz++ = '+'; @@ -5296,7 +5379,7 @@ VpToString(Real *a, char *psz, size_t fFmt, int fPlus) while (psz[-1] == '0') { *(--psz) = 0; } - sprintf(psz, "E%"PRIdSIZE, ex); + sprintf(psz, "e%"PRIdSIZE, ex); if (fFmt) VpFormatSt(pszSav, fFmt); } @@ -5311,7 +5394,7 @@ VpToFString(Real *a, char *psz, size_t fFmt, int fPlus) if (VpToSpecialString(a, psz, fPlus)) return; - if (VpGetSign(a) < 0) *psz++ = '-'; + if (BIGDECIMAL_NEGATIVE_P(a)) *psz++ = '-'; else if (fPlus == 1) *psz++ = ' '; else if (fPlus == 2) *psz++ = '+'; @@ -5733,21 +5816,22 @@ VpSqrt(Real *y, Real *x) ssize_t nr; double val; - /* Zero, NaN or Infinity ? */ - if (!VpHasVal(x)) { - if (VpIsZero(x) || VpGetSign(x) > 0) { - VpAsgn(y,x,1); - goto Exit; - } - VpSetNaN(y); - return VpException(VP_EXCEPTION_OP, "(VpSqrt) SQRT(NaN or negative value)", 0); + /* Zero or +Infinity ? */ + if (VpIsZero(x) || VpIsPosInf(x)) { + VpAsgn(y,x,1); goto Exit; } /* Negative ? */ - if (VpGetSign(x) < 0) { + if (BIGDECIMAL_NEGATIVE_P(x)) { VpSetNaN(y); - return VpException(VP_EXCEPTION_OP, "(VpSqrt) SQRT(negative value)", 0); + return VpException(VP_EXCEPTION_OP, "sqrt of negative value", 0); + } + + /* NaN ? */ + if (VpIsNaN(x)) { + VpSetNaN(y); + return VpException(VP_EXCEPTION_OP, "sqrt of 'NaN'(Not a Number)", 0); } /* One ? */ @@ -5938,10 +6022,10 @@ VpMidRound(Real *y, unsigned short f, ssize_t nf) if (v > 5 || (v == 5 && fracf_1further)) ++div; break; case VP_ROUND_CEIL: - if (fracf && (VpGetSign(y) > 0)) ++div; + if (fracf && BIGDECIMAL_POSITIVE_P(y)) ++div; break; case VP_ROUND_FLOOR: - if (fracf && (VpGetSign(y) < 0)) ++div; + if (fracf && BIGDECIMAL_NEGATIVE_P(y)) ++div; break; case VP_ROUND_HALF_EVEN: /* Banker's rounding */ if (v > 5) ++div; @@ -6060,10 +6144,10 @@ VpInternalRound(Real *c, size_t ixDigit, BDIGIT vPrev, BDIGIT v) if (v >= 6) f = 1; break; case VP_ROUND_CEIL: - if (v && (VpGetSign(c) > 0)) f = 1; + if (v && BIGDECIMAL_POSITIVE_P(c)) f = 1; break; case VP_ROUND_FLOOR: - if (v && (VpGetSign(c) < 0)) f = 1; + if (v && BIGDECIMAL_NEGATIVE_P(c)) f = 1; break; case VP_ROUND_HALF_EVEN: /* Banker's rounding */ /* as per VP_ROUND_HALF_DOWN, because this is the last digit of precision, @@ -6200,7 +6284,7 @@ VpPower(Real *y, Real *x, SIGNED_VALUE n) if (x->exponent == 1 && x->Prec == 1 && x->frac[0] == 1) { /* abs(x) = 1 */ VpSetOne(y); - if (VpGetSign(x) > 0) goto Exit; + if (BIGDECIMAL_POSITIVE_P(x)) goto Exit; if ((n % 2) == 0) goto Exit; VpSetSign(y, -1); goto Exit; diff --git a/ext/bigdecimal/bigdecimal.gemspec b/ext/bigdecimal/bigdecimal.gemspec index e7acfe2d11..98e0125904 100644 --- a/ext/bigdecimal/bigdecimal.gemspec +++ b/ext/bigdecimal/bigdecimal.gemspec @@ -1,23 +1,25 @@ -# -*- ruby -*- -_VERSION = "1.2.8" -date = %w$Date:: $[1] +# coding: utf-8 +_VERSION = '1.3.0.pre' Gem::Specification.new do |s| - s.name = "bigdecimal" - s.version = _VERSION - s.date = date - s.license = 'ruby' - s.summary = "Arbitrary-precision decimal floating-point number library." - s.homepage = "https://www.ruby-lang.org" - s.email = "mrkn@mrkn.jp" - s.description = "This library provides arbitrary-precision decimal floating-point number class." - s.authors = ["Kenta Murata", "Zachary Scott", "Shigeo Kobayashi"] - s.require_path = %[lib] - s.files = %w[ + s.name = "bigdecimal" + s.version = _VERSION + s.authors = ["Kenta Murata", "Zachary Scott", "Shigeo Kobayashi"] + s.email = ["mrkn@mrkn.jp"] + + s.summary = "Arbitrary-precision decimal floating-point number library." + s.description = "This library provides arbitrary-precision decimal floating-point number class." + s.homepage = "https://github.com/ruby/bigdecimal" + s.license = "ruby" + + s.require_paths = %w[lib] + s.extensions = %w[ext/bigdecimal/extconf.rb] + s.files = %w[ bigdecimal.gemspec - bigdecimal.c - bigdecimal.h - depend extconf.rb + ext/bigdecimal/bigdecimal.c + ext/bigdecimal/bigdecimal.h + ext/bigdecimal/depend + ext/bigdecimal/extconf.rb lib/bigdecimal/jacobian.rb lib/bigdecimal/ludcmp.rb lib/bigdecimal/math.rb @@ -27,5 +29,8 @@ Gem::Specification.new do |s| sample/nlsolve.rb sample/pi.rb ] - s.extensions = %w[extconf.rb] + + s.add_development_dependency "rake", "~> 10.0" + s.add_development_dependency "rake-compiler", "~> 0.9" + s.add_development_dependency "minitest", "~> 4.7.5" end diff --git a/ext/bigdecimal/bigdecimal.h b/ext/bigdecimal/bigdecimal.h index f85c3e55ad..fcfeb9c3c3 100644 --- a/ext/bigdecimal/bigdecimal.h +++ b/ext/bigdecimal/bigdecimal.h @@ -90,6 +90,56 @@ llabs(LONG_LONG const x) } #endif +#ifndef HAVE_FINITE +static int +finite(double) +{ + return !isnan(n) && !isinf(n); +} +#endif + +#ifndef isfinite +# ifndef HAVE_ISFINITE +# define HAVE_ISFINITE 1 +# define isfinite(x) finite(x) +# endif +#endif + +#ifndef FIX_CONST_VALUE_PTR +# if defined(__fcc__) || defined(__fcc_version) || \ + defined(__FCC__) || defined(__FCC_VERSION) +/* workaround for old version of Fujitsu C Compiler (fcc) */ +# define FIX_CONST_VALUE_PTR(x) ((const VALUE *)(x)) +# else +# define FIX_CONST_VALUE_PTR(x) (x) +# endif +#endif + +#ifndef HAVE_RB_ARRAY_CONST_PTR +static inline const VALUE * +rb_array_const_ptr(VALUE a) +{ + return FIX_CONST_VALUE_PTR((RBASIC(a)->flags & RARRAY_EMBED_FLAG) ? + RARRAY(a)->as.ary : RARRAY(a)->as.heap.ptr); +} +#endif + +#ifndef RARRAY_CONST_PTR +# define RARRAY_CONST_PTR(a) rb_array_const_ptr(a) +#endif + +#ifndef RARRAY_AREF +# define RARRAY_AREF(a, i) (RARRAY_CONST_PTR(a)[i]) +#endif + +#ifndef HAVE_RB_SYM2STR +static inline VALUE +rb_sym2str(VALUE sym) +{ + return rb_id2str(SYM2ID(sym)); +} +#endif + #ifdef vabs # undef vabs #endif @@ -182,11 +232,11 @@ extern VALUE rb_cBigDecimal; typedef struct { VALUE obj; /* Back pointer(VALUE) for Ruby object. */ size_t MaxPrec; /* Maximum precision size */ - /* This is the actual size of pfrac[] */ + /* This is the actual size of frac[] */ /*(frac[0] to frac[MaxPrec] are available). */ size_t Prec; /* Current precision size. */ - /* This indicates how much the. */ - /* the array frac[] is actually used. */ + /* This indicates how much the */ + /* array frac[] is actually used. */ SIGNED_VALUE exponent; /* Exponent part. */ short sign; /* Attributes of the value. */ /* diff --git a/ext/bigdecimal/depend b/ext/bigdecimal/depend index 3ab229f2b6..6783192b30 100644 --- a/ext/bigdecimal/depend +++ b/ext/bigdecimal/depend @@ -1,7 +1,6 @@ # AUTOGENERATED DEPENDENCIES START bigdecimal.o: $(RUBY_EXTCONF_H) bigdecimal.o: $(arch_hdrdir)/ruby/config.h -bigdecimal.o: $(hdrdir)/ruby/backward.h bigdecimal.o: $(hdrdir)/ruby/defines.h bigdecimal.o: $(hdrdir)/ruby/intern.h bigdecimal.o: $(hdrdir)/ruby/missing.h diff --git a/ext/bigdecimal/extconf.rb b/ext/bigdecimal/extconf.rb index 3799e685bc..b4f3a7a4f8 100644 --- a/ext/bigdecimal/extconf.rb +++ b/ext/bigdecimal/extconf.rb @@ -1,11 +1,30 @@ # frozen_string_literal: false require 'mkmf' +alias __have_macro__ have_macro + have_func("labs", "stdlib.h") have_func("llabs", "stdlib.h") +have_func("finite", "math.h") +have_func("isfinite", "math.h") have_type("struct RRational", "ruby.h") have_func("rb_rational_num", "ruby.h") have_func("rb_rational_den", "ruby.h") +have_func("rb_array_const_ptr", "ruby.h") +have_func("rb_sym2str", "ruby.h") + +have_macro("FIX_CONST_VALUE_PTR", "ruby.h") +have_macro("RARRAY_CONST_PTR", "ruby.h") +have_macro("RARRAY_AREF", "ruby.h") create_makefile('bigdecimal') + +# Add additional dependencies +open('Makefile', 'a') do |io| + if RUBY_VERSION >= '2.4' + io.puts <<-MAKEFILE +bigdecimal.o: $(hdrdir)/ruby/backward.h + MAKEFILE + end +end diff --git a/ext/bigdecimal/lib/bigdecimal/util.rb b/ext/bigdecimal/lib/bigdecimal/util.rb index 0c4e486c00..79e0ebde8b 100644 --- a/ext/bigdecimal/lib/bigdecimal/util.rb +++ b/ext/bigdecimal/lib/bigdecimal/util.rb @@ -1,7 +1,7 @@ # frozen_string_literal: false # BigDecimal extends the native Integer class to provide the #to_d method. # -# When you require the BigDecimal library in your application, this methodwill +# When you require the BigDecimal library in your application, this method will # be available on Integer objects. class Integer < Numeric # call-seq: diff --git a/test/bigdecimal/test_bigdecimal.rb b/test/bigdecimal/test_bigdecimal.rb index 63a3e2747d..dd22997efd 100644 --- a/test/bigdecimal/test_bigdecimal.rb +++ b/test/bigdecimal/test_bigdecimal.rb @@ -53,17 +53,16 @@ class TestBigDecimal < Test::Unit::TestCase assert_equal(1, BigDecimal("1")) assert_equal(1, BigDecimal("1", 1)) assert_raise(ArgumentError) { BigDecimal("1", -1) } - assert_raise(ArgumentError) { BigDecimal(4.2) } - begin - BigDecimal(4.2) - rescue ArgumentError => error - assert_match(/Float/, error.message) - end - assert_raise(ArgumentError) { BigDecimal(42.quo(7)) } - begin - BigDecimal(42.quo(7)) - rescue ArgumentError => error - assert_match(/Rational/, error.message) + end + + def test_global_new_with_invalid_string + [ + '', '.', 'e1', 'd1', '.e', '.d', '1.e', '1.d', '.1e', '.1d', + 'invlaid value' + ].each do |invalid_string| + assert_raise_with_message(ArgumentError, %Q[invalid value for BigDecimal(): "#{invalid_string}"]) do + BigDecimal(invalid_string) + end end end @@ -77,18 +76,28 @@ class TestBigDecimal < Test::Unit::TestCase def test_global_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)) } + assert_raise_with_message(ArgumentError, "can't omit precision for a Rational.") { BigDecimal(42.quo(7)) } end def test_global_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_with_message(ArgumentError, "can't omit precision for a Float.") { BigDecimal(4.2) } assert_raise(ArgumentError) { BigDecimal(0.1, Float::DIG + 2) } assert_nothing_raised { BigDecimal(0.1, Float::DIG + 1) } bug9214 = '[ruby-core:58858]' assert_equal(BigDecimal(-0.0, Float::DIG).sign, -1, bug9214) + + BigDecimal.save_exception_mode do + BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false) + assert_nan(BigDecimal(Float::NAN)) + end + BigDecimal.save_exception_mode do + BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false) + assert_positive_infinite(BigDecimal(Float::INFINITY)) + assert_negative_infinite(BigDecimal(-Float::INFINITY)) + end end def test_global_new_with_big_decimal @@ -116,15 +125,16 @@ class TestBigDecimal < Test::Unit::TestCase assert_equal(1, BigDecimal.new("1", 1)) assert_equal(1, BigDecimal.new(" 1 ")) assert_equal(111, BigDecimal.new("1_1_1_")) - assert_equal(0, BigDecimal.new("_1_1_1")) assert_equal(10**(-1), BigDecimal.new("1E-1"), '#4825') + assert_raise(ArgumentError, /"_1_1_1"/) { BigDecimal.new("_1_1_1") } + BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false) BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false) - assert_equal( 1, BigDecimal.new("Infinity").infinite?) - assert_equal(-1, BigDecimal.new("-Infinity").infinite?) - assert_equal(true, BigDecimal.new("NaN").nan?) - assert_equal( 1, BigDecimal.new("1E1111111111111111111").infinite?) + assert_positive_infinite(BigDecimal.new("Infinity")) + assert_negative_infinite(BigDecimal.new("-Infinity")) + assert_nan(BigDecimal.new("NaN")) + assert_positive_infinite(BigDecimal.new("1E1111111111111111111")) end def test_new_with_integer @@ -392,14 +402,28 @@ class TestBigDecimal < Test::Unit::TestCase end def test_zero_p + BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false) + BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false) + assert_equal(true, BigDecimal.new("0").zero?) + assert_equal(true, BigDecimal.new("-0").zero?) assert_equal(false, BigDecimal.new("1").zero?) assert_equal(true, BigDecimal.new("0E200000000000000").zero?) + assert_equal(false, BigDecimal.new("Infinity").zero?) + assert_equal(false, BigDecimal.new("-Infinity").zero?) + assert_equal(false, BigDecimal.new("NaN").zero?) end def test_nonzero_p + BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false) + BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false) + assert_equal(nil, BigDecimal.new("0").nonzero?) + assert_equal(nil, BigDecimal.new("-0").nonzero?) assert_equal(BigDecimal.new("1"), BigDecimal.new("1").nonzero?) + assert_positive_infinite(BigDecimal.new("Infinity").nonzero?) + assert_negative_infinite(BigDecimal.new("-Infinity").nonzero?) + assert_nan(BigDecimal.new("NaN").nonzero?) end def test_double_fig @@ -457,18 +481,8 @@ class TestBigDecimal < Test::Unit::TestCase BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false) assert_equal(nil, BigDecimal.new("NaN") <=> n1) assert_equal(false, BigDecimal.new("NaN") > n1) - end - - def test_cmp_float_nan - assert_equal(nil, BigDecimal.new("1") <=> Float::NAN) - end - - def test_cmp_float_pos_inf - assert_equal(-1, BigDecimal.new("1") <=> Float::INFINITY) - end - - def test_cmp_float_neg_inf - assert_equal(+1, BigDecimal.new("1") <=> -Float::INFINITY) + assert_equal(nil, BigDecimal.new("NaN") <=> BigDecimal.new("NaN")) + assert_equal(false, BigDecimal.new("NaN") == BigDecimal.new("NaN")) end def test_cmp_failing_coercion @@ -493,6 +507,11 @@ class TestBigDecimal < Test::Unit::TestCase assert_operator(n2, :>, o1) assert_operator(n2, :>=, o1) assert_operator(n1, :>=, 1) + + bug10109 = '[ruby-core:64190]' + BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false) + assert_operator(BigDecimal(0), :<, Float::INFINITY, bug10109) + assert_operator(Float::INFINITY, :>, BigDecimal(0), bug10109) end def test_cmp_bignum @@ -582,7 +601,7 @@ class TestBigDecimal < Test::Unit::TestCase assert_equal(0.0, x.to_f) assert_equal( 1.0 / 0.0, ( 1 / x).to_f) assert_equal(-1.0 / 0.0, (-1 / x).to_f) - assert_equal(true, ( 0 / x).to_f.nan?) + assert_nan(( 0 / x).to_f) x = BigDecimal.new("1") assert_equal(1.0, x.to_f) x = BigDecimal.new((2**100).to_s) @@ -632,6 +651,22 @@ class TestBigDecimal < Test::Unit::TestCase assert_equal(-0.0, BigDecimal('-10e-325').to_f) end + def test_to_r + BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false) + BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false) + + x = BigDecimal.new("0") + assert_kind_of(Rational, x.to_r) + assert_equal(0, x.to_r) + assert_raise(FloatDomainError) {( 1 / x).to_r} + assert_raise(FloatDomainError) {(-1 / x).to_r} + assert_raise(FloatDomainError) {( 0 / x).to_r} + + assert_equal(1, BigDecimal.new("1").to_r) + assert_equal(Rational(3, 2), BigDecimal.new("1.5").to_r) + assert_equal((2**100).to_r, BigDecimal.new((2**100).to_s).to_r) + end + def test_coerce a, b = BigDecimal.new("1").coerce(1.0) assert_instance_of(BigDecimal, a) @@ -654,6 +689,18 @@ class TestBigDecimal < Test::Unit::TestCase assert_equal(x, x.send(:+@)) end + def test_neg + BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false) + BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false) + + assert_equal(BigDecimal.new("-1"), BigDecimal.new("1").send(:-@)) + assert_equal(BigDecimal.new("-0"), BigDecimal.new("0").send(:-@)) + assert_equal(BigDecimal.new("0"), BigDecimal.new("-0").send(:-@)) + assert_equal(BigDecimal.new("-Infinity"), BigDecimal.new("Infinity").send(:-@)) + assert_equal(BigDecimal.new("Infinity"), BigDecimal.new("-Infinity").send(:-@)) + assert_equal(true, BigDecimal.new("NaN").send(:-@).nan?) + end + def test_add x = BigDecimal.new("1") assert_equal(BigDecimal.new("2"), x + x) @@ -666,6 +713,14 @@ class TestBigDecimal < Test::Unit::TestCase x = BigDecimal.new((2**100).to_s) assert_equal(BigDecimal.new((2**100+1).to_s), x + 1) + + BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false) + inf = BigDecimal("Infinity") + neginf = BigDecimal("-Infinity") + + BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, true) + assert_raise_with_message(FloatDomainError, "Computation results to 'Infinity'") { inf + inf } + assert_raise_with_message(FloatDomainError, "Computation results to '-Infinity'") { neginf + neginf } end def test_sub @@ -680,6 +735,14 @@ class TestBigDecimal < Test::Unit::TestCase x = BigDecimal.new((2**100).to_s) assert_equal(BigDecimal.new((2**100-1).to_s), x - 1) + + BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false) + inf = BigDecimal("Infinity") + neginf = BigDecimal("-Infinity") + + BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, true) + assert_raise_with_message(FloatDomainError, "Computation results to 'Infinity'") { inf - neginf } + assert_raise_with_message(FloatDomainError, "Computation results to '-Infinity'") { neginf - inf } end def test_sub_with_float @@ -696,6 +759,14 @@ class TestBigDecimal < Test::Unit::TestCase assert_equal(x, (x * 1).to_i) assert_equal(x, (BigDecimal("1") * x).to_i) assert_equal(BigDecimal.new((2**200).to_s), (x * x).to_i) + + BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false) + inf = BigDecimal("Infinity") + neginf = BigDecimal("-Infinity") + + BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, true) + assert_raise_with_message(FloatDomainError, "Computation results to 'Infinity'") { inf * inf } + assert_raise_with_message(FloatDomainError, "Computation results to '-Infinity'") { neginf * inf } end def test_mult_with_float @@ -723,6 +794,17 @@ class TestBigDecimal < Test::Unit::TestCase assert_equal(BigDecimal('1486.868686869'), BigDecimal('1472.0') / BigDecimal('0.99'), '[ruby-core:59365] [#9316]') assert_equal(4.124045235, BigDecimal('0.9932') / (700 * BigDecimal('0.344045') / BigDecimal('1000.0')), '[#9305]') + + BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false) + assert_positive_zero(BigDecimal.new("1.0") / BigDecimal.new("Infinity")) + assert_negative_zero(BigDecimal.new("-1.0") / BigDecimal.new("Infinity")) + assert_negative_zero(BigDecimal.new("1.0") / BigDecimal.new("-Infinity")) + assert_positive_zero(BigDecimal.new("-1.0") / BigDecimal.new("-Infinity")) + + BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, true) + BigDecimal.mode(BigDecimal::EXCEPTION_ZERODIVIDE, false) + assert_raise_with_message(FloatDomainError, "Computation results to 'Infinity'") { BigDecimal.new("1") / 0 } + assert_raise_with_message(FloatDomainError, "Computation results to '-Infinity'") { BigDecimal.new("-1") / 0 } end def test_div_with_float @@ -807,12 +889,6 @@ class TestBigDecimal < Test::Unit::TestCase BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false) assert_equal(0, BigDecimal("0").div(BigDecimal("Infinity"))) end - - x = BigDecimal.new("1") - y = BigDecimal.new("0.22") - (2..20).each do |i| - assert_equal ("0."+"45"*(i/2)+"5"*(i%2)+"E1"), x.div(y, i).to_s, "#{i}" - end end def test_abs_bigdecimal @@ -822,9 +898,18 @@ class TestBigDecimal < Test::Unit::TestCase assert_equal(1267650600228229401496703205376, x.abs) x = BigDecimal.new("0") assert_equal(0, x.abs) + x = BigDecimal.new("-0") + assert_equal(0, x.abs) + + BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false) + x = BigDecimal.new("Infinity") + assert_equal(BigDecimal.new("Infinity"), x.abs) + x = BigDecimal.new("-Infinity") + assert_equal(BigDecimal.new("Infinity"), x.abs) + BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false) x = BigDecimal.new("NaN") - assert_equal(true, x.abs.nan?) + assert_nan(x.abs) end def test_sqrt_bigdecimal @@ -837,16 +922,19 @@ class TestBigDecimal < Test::Unit::TestCase assert_equal(true, (x.sqrt(200) - y).abs < BigDecimal("1E#{e-200}")) assert_equal(true, (x.sqrt(300) - y).abs < BigDecimal("1E#{e-300}")) x = BigDecimal.new("-" + (2**100).to_s) - assert_raise(FloatDomainError) { x.sqrt(1) } + assert_raise_with_message(FloatDomainError, "sqrt of negative value") { x.sqrt(1) } x = BigDecimal.new((2**200).to_s) assert_equal(2**100, x.sqrt(1)) BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false) BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false) - assert_raise(FloatDomainError) { BigDecimal.new("NaN").sqrt(1) } + assert_raise_with_message(FloatDomainError, "sqrt of 'NaN'(Not a Number)") { BigDecimal.new("NaN").sqrt(1) } + assert_raise_with_message(FloatDomainError, "sqrt of negative value") { BigDecimal.new("-Infinity").sqrt(1) } assert_equal(0, BigDecimal.new("0").sqrt(1)) + assert_equal(0, BigDecimal.new("-0").sqrt(1)) assert_equal(1, BigDecimal.new("1").sqrt(1)) + assert_positive_infinite(BigDecimal.new("Infinity").sqrt(1)) end def test_sqrt_5266 @@ -866,6 +954,7 @@ class TestBigDecimal < Test::Unit::TestCase def test_fix x = BigDecimal.new("1.1") assert_equal(1, x.fix) + assert_kind_of(BigDecimal, x.fix) end def test_frac @@ -873,7 +962,7 @@ class TestBigDecimal < Test::Unit::TestCase assert_equal(0.1, x.frac) assert_equal(0.1, BigDecimal.new("0.1").frac) BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false) - assert_equal(true, BigDecimal.new("NaN").frac.nan?) + assert_nan(BigDecimal.new("NaN").frac) end def test_round @@ -921,6 +1010,75 @@ class TestBigDecimal < Test::Unit::TestCase end end + def test_round_half_even + assert_equal(BigDecimal('12.0'), BigDecimal('12.5').round(half: :even)) + assert_equal(BigDecimal('14.0'), BigDecimal('13.5').round(half: :even)) + + assert_equal(BigDecimal('2.2'), BigDecimal('2.15').round(1, half: :even)) + assert_equal(BigDecimal('2.2'), BigDecimal('2.25').round(1, half: :even)) + assert_equal(BigDecimal('2.4'), BigDecimal('2.35').round(1, half: :even)) + + assert_equal(BigDecimal('-2.2'), BigDecimal('-2.15').round(1, half: :even)) + assert_equal(BigDecimal('-2.2'), BigDecimal('-2.25').round(1, half: :even)) + assert_equal(BigDecimal('-2.4'), BigDecimal('-2.35').round(1, half: :even)) + + assert_equal(BigDecimal('7.1364'), BigDecimal('7.13645').round(4, half: :even)) + assert_equal(BigDecimal('7.1365'), BigDecimal('7.1364501').round(4, half: :even)) + assert_equal(BigDecimal('7.1364'), BigDecimal('7.1364499').round(4, half: :even)) + + assert_equal(BigDecimal('-7.1364'), BigDecimal('-7.13645').round(4, half: :even)) + assert_equal(BigDecimal('-7.1365'), BigDecimal('-7.1364501').round(4, half: :even)) + assert_equal(BigDecimal('-7.1364'), BigDecimal('-7.1364499').round(4, half: :even)) + end + + def test_round_half_up + assert_equal(BigDecimal('13.0'), BigDecimal('12.5').round(half: :up)) + assert_equal(BigDecimal('14.0'), BigDecimal('13.5').round(half: :up)) + + assert_equal(BigDecimal('2.2'), BigDecimal('2.15').round(1, half: :up)) + assert_equal(BigDecimal('2.3'), BigDecimal('2.25').round(1, half: :up)) + assert_equal(BigDecimal('2.4'), BigDecimal('2.35').round(1, half: :up)) + + assert_equal(BigDecimal('-2.2'), BigDecimal('-2.15').round(1, half: :up)) + assert_equal(BigDecimal('-2.3'), BigDecimal('-2.25').round(1, half: :up)) + assert_equal(BigDecimal('-2.4'), BigDecimal('-2.35').round(1, half: :up)) + + assert_equal(BigDecimal('7.1365'), BigDecimal('7.13645').round(4, half: :up)) + assert_equal(BigDecimal('7.1365'), BigDecimal('7.1364501').round(4, half: :up)) + assert_equal(BigDecimal('7.1364'), BigDecimal('7.1364499').round(4, half: :up)) + + assert_equal(BigDecimal('-7.1365'), BigDecimal('-7.13645').round(4, half: :up)) + assert_equal(BigDecimal('-7.1365'), BigDecimal('-7.1364501').round(4, half: :up)) + assert_equal(BigDecimal('-7.1364'), BigDecimal('-7.1364499').round(4, half: :up)) + end + + def test_round_half_down + assert_equal(BigDecimal('12.0'), BigDecimal('12.5').round(half: :down)) + assert_equal(BigDecimal('13.0'), BigDecimal('13.5').round(half: :down)) + + assert_equal(BigDecimal('2.1'), BigDecimal('2.15').round(1, half: :down)) + assert_equal(BigDecimal('2.2'), BigDecimal('2.25').round(1, half: :down)) + assert_equal(BigDecimal('2.3'), BigDecimal('2.35').round(1, half: :down)) + + assert_equal(BigDecimal('-2.1'), BigDecimal('-2.15').round(1, half: :down)) + assert_equal(BigDecimal('-2.2'), BigDecimal('-2.25').round(1, half: :down)) + assert_equal(BigDecimal('-2.3'), BigDecimal('-2.35').round(1, half: :down)) + + assert_equal(BigDecimal('7.1364'), BigDecimal('7.13645').round(4, half: :down)) + assert_equal(BigDecimal('7.1365'), BigDecimal('7.1364501').round(4, half: :down)) + assert_equal(BigDecimal('7.1364'), BigDecimal('7.1364499').round(4, half: :down)) + + assert_equal(BigDecimal('-7.1364'), BigDecimal('-7.13645').round(4, half: :down)) + assert_equal(BigDecimal('-7.1365'), BigDecimal('-7.1364501').round(4, half: :down)) + assert_equal(BigDecimal('-7.1364'), BigDecimal('-7.1364499').round(4, half: :down)) + end + + def test_round_half_invalid_option + assert_raise_with_message(ArgumentError, "invalid rounding mode: invalid") { BigDecimal('12.5').round(half: :invalid) } + assert_raise_with_message(ArgumentError, "invalid rounding mode: invalid") { BigDecimal('2.15').round(1, half: :invalid) } + assert_raise_with_message(ArgumentError, "invalid rounding mode: nil") { BigDecimal('12.5').round(half: nil) } + end + def test_truncate assert_equal(3, BigDecimal.new("3.14159").truncate) assert_equal(8, BigDecimal.new("8.7").truncate) @@ -946,8 +1104,8 @@ class TestBigDecimal < Test::Unit::TestCase assert_equal('-123.45678 90123 45678 9', BigDecimal.new('-123.45678901234567890').to_s('5F')) assert_equal('+123.45678901 23456789', BigDecimal.new('123.45678901234567890').to_s('+8F')) assert_equal(' 123.4567890123456789', BigDecimal.new('123.45678901234567890').to_s(' F')) - assert_equal('0.1234567890123456789E3', BigDecimal.new('123.45678901234567890').to_s) - assert_equal('0.12345 67890 12345 6789E3', BigDecimal.new('123.45678901234567890').to_s(5)) + assert_equal('0.1234567890123456789e3', BigDecimal.new('123.45678901234567890').to_s) + assert_equal('0.12345 67890 12345 6789e3', BigDecimal.new('123.45678901234567890').to_s(5)) end def test_split @@ -969,9 +1127,9 @@ class TestBigDecimal < Test::Unit::TestCase end def test_inspect - x = BigDecimal.new("1234.5678") - prec, maxprec = x.precs - assert_match(/^#$/, x.inspect) + assert_equal("0.123456789012e0", BigDecimal.new("0.123456789012").inspect) + assert_equal("0.123456789012e4", BigDecimal.new("1234.56789012").inspect) + assert_equal("0.123456789012e-4", BigDecimal.new("0.0000123456789012").inspect) end def test_power @@ -1220,23 +1378,23 @@ class TestBigDecimal < Test::Unit::TestCase inf = BigDecimal.new("Infinity") assert_equal(inf, inf + inf) - assert_equal(true, (inf + (-inf)).nan?) - assert_equal(true, (inf - inf).nan?) + assert_nan((inf + (-inf))) + assert_nan((inf - inf)) assert_equal(inf, inf - (-inf)) assert_equal(inf, inf * inf) - assert_equal(true, (inf / inf).nan?) + assert_nan((inf / inf)) assert_equal(inf, inf + 1) assert_equal(inf, inf - 1) assert_equal(inf, inf * 1) - assert_equal(true, (inf * 0).nan?) + assert_nan((inf * 0)) assert_equal(inf, inf / 1) assert_equal(inf, 1 + inf) assert_equal(-inf, 1 - inf) assert_equal(inf, 1 * inf) assert_equal(-inf, -1 * inf) - assert_equal(true, (0 * inf).nan?) + assert_nan((0 * inf)) assert_equal(BigDecimal::SIGN_POSITIVE_ZERO, (1 / inf).sign) assert_equal(BigDecimal::SIGN_NEGATIVE_ZERO, (-1 / inf).sign) end @@ -1306,14 +1464,14 @@ class TestBigDecimal < Test::Unit::TestCase end def test_INFINITY - assert(BigDecimal::INFINITY.infinite?, "BigDecimal::INFINITY is not a infinity") + assert_positive_infinite(BigDecimal::INFINITY) end def test_NAN - assert(BigDecimal::NAN.nan?, "BigDecimal::NAN is not NaN") + assert_nan(BigDecimal::NAN) end - def test_exp_with_zerp_precision + def test_exp_with_zero_precision assert_raise(ArgumentError) do BigMath.exp(1, 0) end @@ -1342,14 +1500,14 @@ class TestBigDecimal < Test::Unit::TestCase BigDecimal.save_exception_mode do BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false) assert(BigMath.exp(BigDecimal::INFINITY, 20) > 0) - assert(BigMath.exp(BigDecimal::INFINITY, 20).infinite?) + assert_positive_infinite(BigMath.exp(BigDecimal::INFINITY, 20)) end end def test_exp_with_nan BigDecimal.save_exception_mode do BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false) - assert(BigMath.exp(BigDecimal::NAN, 20).nan?) + assert_nan(BigMath.exp(BigDecimal::NAN, 20)) end end @@ -1470,21 +1628,21 @@ class TestBigDecimal < Test::Unit::TestCase BigDecimal.save_exception_mode do BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false) assert(BigMath.log(BigDecimal::INFINITY, 20) > 0) - assert(BigMath.log(BigDecimal::INFINITY, 20).infinite?) + assert_positive_infinite(BigMath.log(BigDecimal::INFINITY, 20)) end end def test_BigMath_log_with_nan BigDecimal.save_exception_mode do BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false) - assert(BigMath.log(BigDecimal::NAN, 20).nan?) + assert_nan(BigMath.log(BigDecimal::NAN, 20)) end end def test_BigMath_log_with_float_nan BigDecimal.save_exception_mode do BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false) - assert(BigMath.log(Float::NAN, 20).nan?) + assert_nan(BigMath.log(Float::NAN, 20)) end end