/ext/bigdecimal/bigdecimal.c
C | 7054 lines | 4997 code | 673 blank | 1384 comment | 1210 complexity | f74247c34b39931c1486171c69397292 MD5 | raw file
Possible License(s): LGPL-2.1, AGPL-3.0, 0BSD, Unlicense, GPL-2.0, BSD-3-Clause
Large files files are truncated, but you can click here to view the full file
- /*
- *
- * Ruby BigDecimal(Variable decimal precision) extension library.
- *
- * Copyright(C) 2002 by Shigeo Kobayashi(shigeo@tinyforest.gr.jp)
- *
- */
- /* #define BIGDECIMAL_DEBUG 1 */
- #ifdef BIGDECIMAL_DEBUG
- # define BIGDECIMAL_ENABLE_VPRINT 1
- #endif
- #include "bigdecimal.h"
- #include "ruby/util.h"
- #ifndef BIGDECIMAL_DEBUG
- # undef NDEBUG
- # define NDEBUG
- #endif
- #include <assert.h>
- #include <ctype.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <errno.h>
- #include <math.h>
- #ifdef HAVE_IEEEFP_H
- #include <ieeefp.h>
- #endif
- #include "bits.h"
- #include "static_assert.h"
- /* #define ENABLE_NUMERIC_STRING */
- #define SIGNED_VALUE_MAX INTPTR_MAX
- #define SIGNED_VALUE_MIN INTPTR_MIN
- #define MUL_OVERFLOW_SIGNED_VALUE_P(a, b) MUL_OVERFLOW_SIGNED_INTEGER_P(a, b, SIGNED_VALUE_MIN, SIGNED_VALUE_MAX)
- VALUE rb_cBigDecimal;
- VALUE rb_mBigMath;
- static ID id_BigDecimal_exception_mode;
- static ID id_BigDecimal_rounding_mode;
- static ID id_BigDecimal_precision_limit;
- static ID id_up;
- static ID id_down;
- static ID id_truncate;
- static ID id_half_up;
- static ID id_default;
- static ID id_half_down;
- static ID id_half_even;
- static ID id_banker;
- static ID id_ceiling;
- 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 */
- #ifdef RBIMPL_ATTR_MAYBE_UNUSED
- #define ENTER(n) RBIMPL_ATTR_MAYBE_UNUSED() volatile VALUE vStack[n];int iStack=0
- #else
- #define ENTER(n) volatile VALUE RB_UNUSED_VAR(vStack[n]);int iStack=0
- #endif
- #define PUSH(x) (vStack[iStack++] = (VALUE)(x))
- #define SAVE(p) PUSH((p)->obj)
- #define GUARD_OBJ(p,y) ((p)=(y), SAVE(p))
- #define BASE_FIG BIGDECIMAL_COMPONENT_FIGURES
- #define BASE BIGDECIMAL_BASE
- #define HALF_BASE (BASE/2)
- #define BASE1 (BASE/10)
- #define LOG10_2 0.3010299956639812
- #ifndef RRATIONAL_ZERO_P
- # define RRATIONAL_ZERO_P(x) (FIXNUM_P(rb_rational_num(x)) && \
- FIX2LONG(rb_rational_num(x)) == 0)
- #endif
- #ifndef RRATIONAL_NEGATIVE_P
- # define RRATIONAL_NEGATIVE_P(x) RTEST(rb_funcall((x), '<', 1, INT2FIX(0)))
- #endif
- #ifndef DECIMAL_SIZE_OF_BITS
- #define DECIMAL_SIZE_OF_BITS(n) (((n) * 3010 + 9998) / 9999)
- /* an approximation of ceil(n * log10(2)), upto 65536 at least */
- #endif
- #ifdef PRIsVALUE
- # define RB_OBJ_CLASSNAME(obj) rb_obj_class(obj)
- # define RB_OBJ_STRING(obj) (obj)
- #else
- # define PRIsVALUE "s"
- # define RB_OBJ_CLASSNAME(obj) rb_obj_classname(obj)
- # define RB_OBJ_STRING(obj) StringValueCStr(obj)
- #endif
- #define BIGDECIMAL_POSITIVE_P(bd) ((bd)->sign > 0)
- #define BIGDECIMAL_NEGATIVE_P(bd) ((bd)->sign < 0)
- /*
- * ================== Ruby Interface part ==========================
- */
- #define DoSomeOne(x,y,f) rb_num_coerce_bin(x,y,f)
- /*
- * VP routines used in BigDecimal part
- */
- static unsigned short VpGetException(void);
- static void VpSetException(unsigned short f);
- static void VpInternalRound(Real *c, size_t ixDigit, DECDIG vPrev, DECDIG v);
- static int VpLimitRound(Real *c, size_t ixDigit);
- static Real *VpCopy(Real *pv, Real const* const x);
- #ifdef BIGDECIMAL_ENABLE_VPRINT
- static int VPrint(FILE *fp,const char *cntl_chr,Real *a);
- #endif
- /*
- * **** BigDecimal part ****
- */
- static VALUE BigDecimal_nan(void);
- static VALUE BigDecimal_positive_infinity(void);
- static VALUE BigDecimal_negative_infinity(void);
- static VALUE BigDecimal_positive_zero(void);
- static VALUE BigDecimal_negative_zero(void);
- static void
- BigDecimal_delete(void *pv)
- {
- VpFree(pv);
- }
- static size_t
- BigDecimal_memsize(const void *ptr)
- {
- const Real *pv = ptr;
- return (sizeof(*pv) + pv->MaxPrec * sizeof(DECDIG));
- }
- #ifndef HAVE_RB_EXT_RACTOR_SAFE
- # undef RUBY_TYPED_FROZEN_SHAREABLE
- # define RUBY_TYPED_FROZEN_SHAREABLE 0
- #endif
- static const rb_data_type_t BigDecimal_data_type = {
- "BigDecimal",
- { 0, BigDecimal_delete, BigDecimal_memsize, },
- #ifdef RUBY_TYPED_FREE_IMMEDIATELY
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_FROZEN_SHAREABLE
- #endif
- };
- static inline int
- is_kind_of_BigDecimal(VALUE const v)
- {
- return rb_typeddata_is_kind_of(v, &BigDecimal_data_type);
- }
- static void
- VpCheckException(Real *p, bool always)
- {
- if (VpIsNaN(p)) {
- VpException(VP_EXCEPTION_NaN, "Computation results to 'NaN'(Not a Number)", always);
- }
- else if (VpIsPosInf(p)) {
- VpException(VP_EXCEPTION_INFINITY, "Computation results to 'Infinity'", always);
- }
- else if (VpIsNegInf(p)) {
- VpException(VP_EXCEPTION_INFINITY, "Computation results to '-Infinity'", always);
- }
- }
- static VALUE
- VpCheckGetValue(Real *p)
- {
- VpCheckException(p, false);
- return p->obj;
- }
- NORETURN(static void cannot_be_coerced_into_BigDecimal(VALUE, VALUE));
- static void
- cannot_be_coerced_into_BigDecimal(VALUE exc_class, VALUE v)
- {
- VALUE str;
- if (rb_special_const_p(v)) {
- str = rb_inspect(v);
- }
- else {
- str = rb_class_name(rb_obj_class(v));
- }
- str = rb_str_cat2(rb_str_dup(str), " can't be coerced into BigDecimal");
- rb_exc_raise(rb_exc_new3(exc_class, str));
- }
- static inline VALUE BigDecimal_div2(VALUE, VALUE, VALUE);
- static VALUE rb_inum_convert_to_BigDecimal(VALUE val, size_t digs, int raise_exception);
- static VALUE rb_float_convert_to_BigDecimal(VALUE val, size_t digs, int raise_exception);
- static VALUE rb_rational_convert_to_BigDecimal(VALUE val, size_t digs, int raise_exception);
- static VALUE rb_cstr_convert_to_BigDecimal(const char *c_str, size_t digs, int raise_exception);
- static VALUE rb_convert_to_BigDecimal(VALUE val, size_t digs, int raise_exception);
- static Real*
- GetVpValueWithPrec(VALUE v, long prec, int must)
- {
- const size_t digs = prec < 0 ? SIZE_MAX : (size_t)prec;
- switch(TYPE(v)) {
- case T_FLOAT:
- v = rb_float_convert_to_BigDecimal(v, digs, must);
- break;
- case T_RATIONAL:
- v = rb_rational_convert_to_BigDecimal(v, digs, must);
- break;
- case T_DATA:
- if (!is_kind_of_BigDecimal(v)) {
- goto SomeOneMayDoIt;
- }
- break;
- case T_FIXNUM: {
- char szD[128];
- sprintf(szD, "%ld", FIX2LONG(v));
- v = rb_cstr_convert_to_BigDecimal(szD, VpBaseFig() * 2 + 1, must);
- break;
- }
- #ifdef ENABLE_NUMERIC_STRING
- case T_STRING: {
- const char *c_str = StringValueCStr(v);
- v = rb_cstr_convert_to_BigDecimal(c_str, RSTRING_LEN(v) + VpBaseFig() + 1, must);
- break;
- }
- #endif /* ENABLE_NUMERIC_STRING */
- case T_BIGNUM: {
- VALUE bg = rb_big2str(v, 10);
- v = rb_cstr_convert_to_BigDecimal(RSTRING_PTR(bg), RSTRING_LEN(bg) + VpBaseFig() + 1, must);
- RB_GC_GUARD(bg);
- break;
- }
- default:
- goto SomeOneMayDoIt;
- }
- Real *vp;
- TypedData_Get_Struct(v, Real, &BigDecimal_data_type, vp);
- return vp;
- SomeOneMayDoIt:
- if (must) {
- cannot_be_coerced_into_BigDecimal(rb_eTypeError, v);
- }
- return NULL; /* NULL means to coerce */
- }
- static Real*
- GetVpValue(VALUE v, int must)
- {
- return GetVpValueWithPrec(v, -1, must);
- }
- /* call-seq:
- * BigDecimal.double_fig
- *
- * The BigDecimal.double_fig class method returns the number of digits a
- * Float number is allowed to have. The result depends upon the CPU and OS
- * in use.
- */
- static VALUE
- BigDecimal_double_fig(VALUE self)
- {
- return INT2FIX(VpDblFig());
- }
- /* call-seq:
- * big_decimal.precs -> array
- *
- * Returns an Array of two Integer values that represent platform-dependent
- * internal storage properties.
- *
- * This method is deprecated and will be removed in the future.
- * Instead, use BigDecimal#n_significant_digits for obtaining the number of
- * significant digits in scientific notation, and BigDecimal#precision for
- * obtaining the number of digits in decimal notation.
- *
- * BigDecimal('5').precs #=> [9, 18]
- */
- static VALUE
- BigDecimal_prec(VALUE self)
- {
- ENTER(1);
- Real *p;
- VALUE obj;
- rb_category_warn(RB_WARN_CATEGORY_DEPRECATED,
- "BigDecimal#precs is deprecated and will be removed in the future; "
- "use BigDecimal#precision instead.");
- GUARD_OBJ(p, GetVpValue(self, 1));
- obj = rb_assoc_new(SIZET2NUM(p->Prec*VpBaseFig()),
- SIZET2NUM(p->MaxPrec*VpBaseFig()));
- return obj;
- }
- /*
- * call-seq:
- * big_decimal.precision -> intreger
- *
- * Returns the number of decimal digits in this number.
- *
- * Example:
- *
- * BigDecimal("0").precision # => 0
- * BigDecimal("1").precision # => 1
- * BigDecimal("-1e20").precision # => 21
- * BigDecimal("1e-20").precision # => 20
- * BigDecimal("Infinity").precision # => 0
- * BigDecimal("-Infinity").precision # => 0
- * BigDecimal("NaN").precision # => 0
- */
- static VALUE
- BigDecimal_precision(VALUE self)
- {
- ENTER(1);
- Real *p;
- GUARD_OBJ(p, GetVpValue(self, 1));
- /*
- * The most significant digit is frac[0], and the least significant digit is frac[Prec-1].
- * When the exponent is zero, the decimal point is located just before frac[0].
- * When the exponent is negative, the decimal point moves to leftward.
- * Conversely, when the exponent is positive, the decimal point moves to rightward.
- *
- * | frac[0] frac[1] frac[2] . frac[3] frac[4] ... frac[Prec-1]
- * |------------------------> exponent == 3
- */
- ssize_t ex = p->exponent;
- ssize_t precision = 0;
- if (ex < 0) {
- precision = (-ex + 1) * BASE_FIG; /* 1 is for p->frac[0] */
- ex = 0;
- }
- else if (p->Prec > 0) {
- DECDIG x = p->frac[0];
- for (precision = 0; x > 0; x /= 10) {
- ++precision;
- }
- }
- if (ex > (ssize_t)p->Prec) {
- precision += (ex - 1) * BASE_FIG;
- }
- else if (p->Prec > 0) {
- ssize_t n = (ssize_t)p->Prec - 1;
- while (n > 0 && p->frac[n] == 0) --n;
- precision += n * BASE_FIG;
- if (ex < (ssize_t)p->Prec) {
- DECDIG x = p->frac[n];
- for (; x > 0 && x % 10 == 0; x /= 10) {
- --precision;
- }
- }
- }
- return SSIZET2NUM(precision);
- }
- static VALUE
- BigDecimal_n_significant_digits(VALUE self)
- {
- ENTER(1);
- Real *p;
- GUARD_OBJ(p, GetVpValue(self, 1));
- ssize_t n = p->Prec;
- while (n > 0 && p->frac[n-1] == 0) --n;
- if (n <= 0) {
- return INT2FIX(0);
- }
- int nlz, ntz;
- DECDIG x = p->frac[0];
- for (nlz = BASE_FIG; x > 0; x /= 10) --nlz;
- x = p->frac[n-1];
- for (ntz = 0; x > 0 && x % 10 == 0; x /= 10) ++ntz;
- ssize_t n_digits = BASE_FIG * n - nlz - ntz;
- return SSIZET2NUM(n_digits);
- }
- /*
- * call-seq: hash
- *
- * Creates a hash for this BigDecimal.
- *
- * Two BigDecimals with equal sign,
- * fractional part and exponent have the same hash.
- */
- static VALUE
- BigDecimal_hash(VALUE self)
- {
- ENTER(1);
- Real *p;
- st_index_t hash;
- GUARD_OBJ(p, GetVpValue(self, 1));
- hash = (st_index_t)p->sign;
- /* hash!=2: the case for 0(1),NaN(0) or +-Infinity(3) is sign itself */
- if(hash == 2 || hash == (st_index_t)-2) {
- hash ^= rb_memhash(p->frac, sizeof(DECDIG)*p->Prec);
- hash += p->exponent;
- }
- return ST2FIX(hash);
- }
- /*
- * call-seq: _dump
- *
- * Method used to provide marshalling support.
- *
- * inf = BigDecimal('Infinity')
- * #=> Infinity
- * BigDecimal._load(inf._dump)
- * #=> Infinity
- *
- * See the Marshal module.
- */
- static VALUE
- BigDecimal_dump(int argc, VALUE *argv, VALUE self)
- {
- ENTER(5);
- Real *vp;
- char *psz;
- VALUE dummy;
- volatile VALUE dump;
- rb_scan_args(argc, argv, "01", &dummy);
- GUARD_OBJ(vp,GetVpValue(self, 1));
- dump = rb_str_new(0, VpNumOfChars(vp, "E")+50);
- psz = RSTRING_PTR(dump);
- sprintf(psz, "%"PRIuSIZE":", VpMaxPrec(vp)*VpBaseFig());
- VpToString(vp, psz+strlen(psz), 0, 0);
- rb_str_resize(dump, strlen(psz));
- return dump;
- }
- /*
- * Internal method used to provide marshalling support. See the Marshal module.
- */
- static VALUE
- BigDecimal_load(VALUE self, VALUE str)
- {
- ENTER(2);
- Real *pv;
- unsigned char *pch;
- unsigned char ch;
- unsigned long m=0;
- pch = (unsigned char *)StringValueCStr(str);
- /* First get max prec */
- while((*pch) != (unsigned char)'\0' && (ch = *pch++) != (unsigned char)':') {
- if(!ISDIGIT(ch)) {
- rb_raise(rb_eTypeError, "load failed: invalid character in the marshaled string");
- }
- m = m*10 + (unsigned long)(ch-'0');
- }
- if (m > VpBaseFig()) m -= VpBaseFig();
- GUARD_OBJ(pv, VpNewRbClass(m, (char *)pch, self, true, true));
- m /= VpBaseFig();
- if (m && pv->MaxPrec > m) {
- pv->MaxPrec = m+1;
- }
- return VpCheckGetValue(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 || NIL_P(mode))
- 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;
- break;
- 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)
- {
- unsigned short sw;
- ID id;
- switch (TYPE(v)) {
- case T_SYMBOL:
- id = SYM2ID(v);
- if (id == id_up)
- return VP_ROUND_UP;
- if (id == id_down || id == id_truncate)
- return VP_ROUND_DOWN;
- if (id == id_half_up || id == id_default)
- return VP_ROUND_HALF_UP;
- if (id == id_half_down)
- return VP_ROUND_HALF_DOWN;
- if (id == id_half_even || id == id_banker)
- return VP_ROUND_HALF_EVEN;
- if (id == id_ceiling || id == id_ceil)
- return VP_ROUND_CEIL;
- if (id == id_floor)
- return VP_ROUND_FLOOR;
- rb_raise(rb_eArgError, "invalid rounding mode");
- default:
- break;
- }
- sw = NUM2USHORT(v);
- if (!VpIsRoundMode(sw)) {
- rb_raise(rb_eArgError, "invalid rounding mode");
- }
- return sw;
- }
- /* call-seq:
- * BigDecimal.mode(mode, value)
- *
- * Controls handling of arithmetic exceptions and rounding. If no value
- * is supplied, the current value is returned.
- *
- * Six values of the mode parameter control the handling of arithmetic
- * exceptions:
- *
- * BigDecimal::EXCEPTION_NaN
- * BigDecimal::EXCEPTION_INFINITY
- * BigDecimal::EXCEPTION_UNDERFLOW
- * BigDecimal::EXCEPTION_OVERFLOW
- * BigDecimal::EXCEPTION_ZERODIVIDE
- * BigDecimal::EXCEPTION_ALL
- *
- * For each mode parameter above, if the value set is false, computation
- * continues after an arithmetic exception of the appropriate type.
- * When computation continues, results are as follows:
- *
- * EXCEPTION_NaN:: NaN
- * EXCEPTION_INFINITY:: +Infinity or -Infinity
- * EXCEPTION_UNDERFLOW:: 0
- * EXCEPTION_OVERFLOW:: +Infinity or -Infinity
- * EXCEPTION_ZERODIVIDE:: +Infinity or -Infinity
- *
- * One value of the mode parameter controls the rounding of numeric values:
- * BigDecimal::ROUND_MODE. The values it can take are:
- *
- * ROUND_UP, :up:: round away from zero
- * ROUND_DOWN, :down, :truncate:: round towards zero (truncate)
- * ROUND_HALF_UP, :half_up, :default:: round towards the nearest neighbor, unless both neighbors are equidistant, in which case round away from zero. (default)
- * ROUND_HALF_DOWN, :half_down:: round towards the nearest neighbor, unless both neighbors are equidistant, in which case round towards zero.
- * ROUND_HALF_EVEN, :half_even, :banker:: round towards the nearest neighbor, unless both neighbors are equidistant, in which case round towards the even neighbor (Banker's rounding)
- * ROUND_CEILING, :ceiling, :ceil:: round towards positive infinity (ceil)
- * ROUND_FLOOR, :floor:: round towards negative infinity (floor)
- *
- */
- static VALUE
- BigDecimal_mode(int argc, VALUE *argv, VALUE self)
- {
- VALUE which;
- VALUE val;
- unsigned long f,fo;
- rb_scan_args(argc, argv, "11", &which, &val);
- f = (unsigned long)NUM2INT(which);
- if (f & VP_EXCEPTION_ALL) {
- /* Exception mode setting */
- fo = VpGetException();
- if (val == Qnil) return INT2FIX(fo);
- if (val != Qfalse && val!=Qtrue) {
- rb_raise(rb_eArgError, "second argument must be true or false");
- return Qnil; /* Not reached */
- }
- if (f & VP_EXCEPTION_INFINITY) {
- VpSetException((unsigned short)((val == Qtrue) ? (fo | VP_EXCEPTION_INFINITY) :
- (fo & (~VP_EXCEPTION_INFINITY))));
- }
- fo = VpGetException();
- if (f & VP_EXCEPTION_NaN) {
- VpSetException((unsigned short)((val == Qtrue) ? (fo | VP_EXCEPTION_NaN) :
- (fo & (~VP_EXCEPTION_NaN))));
- }
- fo = VpGetException();
- if (f & VP_EXCEPTION_UNDERFLOW) {
- VpSetException((unsigned short)((val == Qtrue) ? (fo | VP_EXCEPTION_UNDERFLOW) :
- (fo & (~VP_EXCEPTION_UNDERFLOW))));
- }
- fo = VpGetException();
- if(f & VP_EXCEPTION_ZERODIVIDE) {
- VpSetException((unsigned short)((val == Qtrue) ? (fo | VP_EXCEPTION_ZERODIVIDE) :
- (fo & (~VP_EXCEPTION_ZERODIVIDE))));
- }
- fo = VpGetException();
- return INT2FIX(fo);
- }
- if (VP_ROUND_MODE == f) {
- /* Rounding mode setting */
- unsigned short sw;
- fo = VpGetRoundMode();
- if (NIL_P(val)) return INT2FIX(fo);
- sw = check_rounding_mode(val);
- fo = VpSetRoundMode(sw);
- return INT2FIX(fo);
- }
- rb_raise(rb_eTypeError, "first argument for BigDecimal.mode invalid");
- return Qnil;
- }
- static size_t
- GetAddSubPrec(Real *a, Real *b)
- {
- size_t mxs;
- size_t mx = a->Prec;
- SIGNED_VALUE d;
- if (!VpIsDef(a) || !VpIsDef(b)) return (size_t)-1L;
- if (mx < b->Prec) mx = b->Prec;
- if (a->exponent != b->exponent) {
- mxs = mx;
- d = a->exponent - b->exponent;
- if (d < 0) d = -d;
- mx = mx + (size_t)d;
- if (mx < mxs) {
- return VpException(VP_EXCEPTION_INFINITY, "Exponent overflow", 0);
- }
- }
- return mx;
- }
- static SIGNED_VALUE
- GetPrecisionInt(VALUE v)
- {
- SIGNED_VALUE n;
- n = NUM2INT(v);
- if (n < 0) {
- rb_raise(rb_eArgError, "negative precision");
- }
- return n;
- }
- static VALUE
- BigDecimal_wrap_struct(VALUE obj, Real *vp)
- {
- assert(is_kind_of_BigDecimal(obj));
- assert(vp != NULL);
- if (vp->obj == obj && RTYPEDDATA_DATA(obj) == vp)
- return obj;
- assert(RTYPEDDATA_DATA(obj) == NULL);
- assert(vp->obj == 0);
- RTYPEDDATA_DATA(obj) = vp;
- vp->obj = obj;
- RB_OBJ_FREEZE(obj);
- return obj;
- }
- VP_EXPORT Real *
- VpNewRbClass(size_t mx, const char *str, VALUE klass, bool strict_p, bool raise_exception)
- {
- VALUE obj = TypedData_Wrap_Struct(klass, &BigDecimal_data_type, 0);
- Real *pv = VpAlloc(mx, str, strict_p, raise_exception);
- if (!pv)
- return NULL;
- BigDecimal_wrap_struct(obj, pv);
- return pv;
- }
- VP_EXPORT Real *
- VpCreateRbObject(size_t mx, const char *str, bool raise_exception)
- {
- return VpNewRbClass(mx, str, rb_cBigDecimal, true, raise_exception);
- }
- #define VpAllocReal(prec) (Real *)VpMemAlloc(offsetof(Real, frac) + (prec) * sizeof(DECDIG))
- #define VpReallocReal(ptr, prec) (Real *)VpMemRealloc((ptr), offsetof(Real, frac) + (prec) * sizeof(DECDIG))
- static Real *
- VpCopy(Real *pv, Real const* const x)
- {
- assert(x != NULL);
- pv = VpReallocReal(pv, x->MaxPrec);
- pv->MaxPrec = x->MaxPrec;
- pv->Prec = x->Prec;
- pv->exponent = x->exponent;
- pv->sign = x->sign;
- pv->flag = x->flag;
- MEMCPY(pv->frac, x->frac, DECDIG, pv->MaxPrec);
- return pv;
- }
- /* Returns True if the value is Not a Number. */
- static VALUE
- BigDecimal_IsNaN(VALUE self)
- {
- Real *p = GetVpValue(self, 1);
- if (VpIsNaN(p)) return Qtrue;
- return Qfalse;
- }
- /* Returns nil, -1, or +1 depending on whether the value is finite,
- * -Infinity, or +Infinity.
- */
- static VALUE
- BigDecimal_IsInfinite(VALUE self)
- {
- Real *p = GetVpValue(self, 1);
- if (VpIsPosInf(p)) return INT2FIX(1);
- if (VpIsNegInf(p)) return INT2FIX(-1);
- return Qnil;
- }
- /* Returns True if the value is finite (not NaN or infinite). */
- static VALUE
- BigDecimal_IsFinite(VALUE self)
- {
- Real *p = GetVpValue(self, 1);
- if (VpIsNaN(p)) return Qfalse;
- if (VpIsInf(p)) return Qfalse;
- return Qtrue;
- }
- static void
- BigDecimal_check_num(Real *p)
- {
- VpCheckException(p, true);
- }
- static VALUE BigDecimal_split(VALUE self);
- /* Returns the value as an Integer.
- *
- * If the BigDecimal is infinity or NaN, raises FloatDomainError.
- */
- static VALUE
- BigDecimal_to_i(VALUE self)
- {
- ENTER(5);
- ssize_t e, nf;
- Real *p;
- GUARD_OBJ(p, GetVpValue(self, 1));
- BigDecimal_check_num(p);
- e = VpExponent10(p);
- if (e <= 0) return INT2FIX(0);
- nf = VpBaseFig();
- if (e <= nf) {
- return LONG2NUM((long)(VpGetSign(p) * (DECDIG_DBL_SIGNED)p->frac[0]));
- }
- else {
- VALUE a = BigDecimal_split(self);
- VALUE digits = RARRAY_AREF(a, 1);
- VALUE numerator = rb_funcall(digits, rb_intern("to_i"), 0);
- VALUE ret;
- ssize_t dpower = e - (ssize_t)RSTRING_LEN(digits);
- if (BIGDECIMAL_NEGATIVE_P(p)) {
- numerator = rb_funcall(numerator, '*', 1, INT2FIX(-1));
- }
- if (dpower < 0) {
- ret = rb_funcall(numerator, rb_intern("div"), 1,
- rb_funcall(INT2FIX(10), rb_intern("**"), 1,
- INT2FIX(-dpower)));
- }
- else {
- ret = rb_funcall(numerator, '*', 1,
- rb_funcall(INT2FIX(10), rb_intern("**"), 1,
- INT2FIX(dpower)));
- }
- if (RB_TYPE_P(ret, T_FLOAT)) {
- rb_raise(rb_eFloatDomainError, "Infinity");
- }
- return ret;
- }
- }
- /* Returns a new Float object having approximately the same value as the
- * BigDecimal number. Normal accuracy limits and built-in errors of binary
- * Float arithmetic apply.
- */
- static VALUE
- BigDecimal_to_f(VALUE self)
- {
- ENTER(1);
- Real *p;
- double d;
- SIGNED_VALUE e;
- char *buf;
- volatile VALUE str;
- GUARD_OBJ(p, GetVpValue(self, 1));
- if (VpVtoD(&d, &e, p) != 1)
- return rb_float_new(d);
- if (e > (SIGNED_VALUE)(DBL_MAX_10_EXP+BASE_FIG))
- goto overflow;
- if (e < (SIGNED_VALUE)(DBL_MIN_10_EXP-BASE_FIG))
- goto underflow;
- str = rb_str_new(0, VpNumOfChars(p, "E"));
- buf = RSTRING_PTR(str);
- VpToString(p, buf, 0, 0);
- errno = 0;
- d = strtod(buf, 0);
- if (errno == ERANGE) {
- if (d == 0.0) goto underflow;
- if (fabs(d) >= HUGE_VAL) goto overflow;
- }
- return rb_float_new(d);
- overflow:
- VpException(VP_EXCEPTION_OVERFLOW, "BigDecimal to Float conversion", 0);
- 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 (BIGDECIMAL_NEGATIVE_P(p))
- return rb_float_new(-0.0);
- else
- return rb_float_new(0.0);
- }
- /* Converts a BigDecimal to a Rational.
- */
- static VALUE
- BigDecimal_to_r(VALUE self)
- {
- Real *p;
- ssize_t sign, power, denomi_power;
- VALUE a, digits, numerator;
- p = GetVpValue(self, 1);
- BigDecimal_check_num(p);
- sign = VpGetSign(p);
- power = VpExponent10(p);
- a = BigDecimal_split(self);
- digits = RARRAY_AREF(a, 1);
- denomi_power = power - RSTRING_LEN(digits);
- numerator = rb_funcall(digits, rb_intern("to_i"), 0);
- if (sign < 0) {
- numerator = rb_funcall(numerator, '*', 1, INT2FIX(-1));
- }
- if (denomi_power < 0) {
- return rb_Rational(numerator,
- rb_funcall(INT2FIX(10), rb_intern("**"), 1,
- INT2FIX(-denomi_power)));
- }
- else {
- return rb_Rational1(rb_funcall(numerator, '*', 1,
- rb_funcall(INT2FIX(10), rb_intern("**"), 1,
- INT2FIX(denomi_power))));
- }
- }
- /* The coerce method provides support for Ruby type coercion. It is not
- * enabled by default.
- *
- * This means that binary operations like + * / or - can often be performed
- * on a BigDecimal and an object of another type, if the other object can
- * be coerced into a BigDecimal value.
- *
- * e.g.
- * a = BigDecimal("1.0")
- * b = a / 2.0 #=> 0.5
- *
- * Note that coercing a String to a BigDecimal is not supported by default;
- * it requires a special compile-time option when building Ruby.
- */
- static VALUE
- BigDecimal_coerce(VALUE self, VALUE other)
- {
- ENTER(2);
- VALUE obj;
- Real *b;
- if (RB_TYPE_P(other, T_FLOAT)) {
- GUARD_OBJ(b, GetVpValueWithPrec(other, 0, 1));
- obj = rb_assoc_new(VpCheckGetValue(b), self);
- }
- else {
- if (RB_TYPE_P(other, T_RATIONAL)) {
- Real* pv = DATA_PTR(self);
- GUARD_OBJ(b, GetVpValueWithPrec(other, pv->Prec*VpBaseFig(), 1));
- }
- else {
- GUARD_OBJ(b, GetVpValue(other, 1));
- }
- obj = rb_assoc_new(b->obj, self);
- }
- return obj;
- }
- /*
- * call-seq:
- * +big_decimal -> big_decimal
- *
- * Return self.
- *
- * +BigDecimal('5') #=> 0.5e1
- */
- static VALUE
- BigDecimal_uplus(VALUE self)
- {
- return self;
- }
- /*
- * Document-method: BigDecimal#add
- * Document-method: BigDecimal#+
- *
- * call-seq:
- * add(value, digits)
- *
- * Add the specified value.
- *
- * e.g.
- * c = a.add(b,n)
- * c = a + b
- *
- * digits:: If specified and less than the number of significant digits of the
- * result, the result is rounded to that number of digits, according
- * to BigDecimal.mode.
- */
- static VALUE
- BigDecimal_add(VALUE self, VALUE r)
- {
- ENTER(5);
- Real *c, *a, *b;
- size_t mx;
- GUARD_OBJ(a, GetVpValue(self, 1));
- if (RB_TYPE_P(r, T_FLOAT)) {
- b = GetVpValueWithPrec(r, 0, 1);
- }
- else if (RB_TYPE_P(r, T_RATIONAL)) {
- b = GetVpValueWithPrec(r, a->Prec*VpBaseFig(), 1);
- }
- else {
- b = GetVpValue(r, 0);
- }
- if (!b) return DoSomeOne(self,r,'+');
- SAVE(b);
- if (VpIsNaN(b)) return b->obj;
- if (VpIsNaN(a)) return a->obj;
- mx = GetAddSubPrec(a, b);
- if (mx == (size_t)-1L) {
- GUARD_OBJ(c, VpCreateRbObject(VpBaseFig() + 1, "0", true));
- VpAddSub(c, a, b, 1);
- }
- else {
- GUARD_OBJ(c, VpCreateRbObject(mx * (VpBaseFig() + 1), "0", true));
- if(!mx) {
- VpSetInf(c, VpGetSign(a));
- }
- else {
- VpAddSub(c, a, b, 1);
- }
- }
- return VpCheckGetValue(c);
- }
- /* call-seq:
- * a - b -> bigdecimal
- *
- * Subtract the specified value.
- *
- * e.g.
- * c = a - b
- *
- * The precision of the result value depends on the type of +b+.
- *
- * If +b+ is a Float, the precision of the result is Float::DIG+1.
- *
- * If +b+ is a BigDecimal, the precision of the result is +b+'s precision of
- * internal representation from platform. So, it's return value is platform
- * dependent.
- *
- */
- static VALUE
- BigDecimal_sub(VALUE self, VALUE r)
- {
- ENTER(5);
- Real *c, *a, *b;
- size_t mx;
- GUARD_OBJ(a, GetVpValue(self,1));
- if (RB_TYPE_P(r, T_FLOAT)) {
- b = GetVpValueWithPrec(r, 0, 1);
- }
- else if (RB_TYPE_P(r, T_RATIONAL)) {
- b = GetVpValueWithPrec(r, a->Prec*VpBaseFig(), 1);
- }
- else {
- b = GetVpValue(r,0);
- }
- if (!b) return DoSomeOne(self,r,'-');
- SAVE(b);
- if (VpIsNaN(b)) return b->obj;
- if (VpIsNaN(a)) return a->obj;
- mx = GetAddSubPrec(a,b);
- if (mx == (size_t)-1L) {
- GUARD_OBJ(c, VpCreateRbObject(VpBaseFig() + 1, "0", true));
- VpAddSub(c, a, b, -1);
- }
- else {
- GUARD_OBJ(c,VpCreateRbObject(mx *(VpBaseFig() + 1), "0", true));
- if (!mx) {
- VpSetInf(c,VpGetSign(a));
- }
- else {
- VpAddSub(c, a, b, -1);
- }
- }
- return VpCheckGetValue(c);
- }
- static VALUE
- BigDecimalCmp(VALUE self, VALUE r,char op)
- {
- ENTER(5);
- SIGNED_VALUE e;
- Real *a, *b=0;
- GUARD_OBJ(a, GetVpValue(self, 1));
- switch (TYPE(r)) {
- case T_DATA:
- if (!is_kind_of_BigDecimal(r)) break;
- /* fall through */
- case T_FIXNUM:
- /* fall through */
- case T_BIGNUM:
- GUARD_OBJ(b, GetVpValue(r, 0));
- break;
- case T_FLOAT:
- GUARD_OBJ(b, GetVpValueWithPrec(r, 0, 0));
- break;
- case T_RATIONAL:
- GUARD_OBJ(b, GetVpValueWithPrec(r, a->Prec*VpBaseFig(), 0));
- break;
- default:
- break;
- }
- if (b == NULL) {
- ID f = 0;
- switch (op) {
- case '*':
- return rb_num_coerce_cmp(self, r, rb_intern("<=>"));
- case '=':
- return RTEST(rb_num_coerce_cmp(self, r, rb_intern("=="))) ? Qtrue : Qfalse;
- case 'G':
- f = rb_intern(">=");
- break;
- case 'L':
- f = rb_intern("<=");
- break;
- case '>':
- /* fall through */
- case '<':
- f = (ID)op;
- break;
- default:
- break;
- }
- return rb_num_coerce_relop(self, r, f);
- }
- SAVE(b);
- e = VpComp(a, b);
- if (e == 999)
- return (op == '*') ? Qnil : Qfalse;
- switch (op) {
- case '*':
- return INT2FIX(e); /* any op */
- case '=':
- if (e == 0) return Qtrue;
- return Qfalse;
- case 'G':
- if (e >= 0) return Qtrue;
- return Qfalse;
- case '>':
- if (e > 0) return Qtrue;
- return Qfalse;
- case 'L':
- if (e <= 0) return Qtrue;
- return Qfalse;
- case '<':
- if (e < 0) return Qtrue;
- return Qfalse;
- default:
- break;
- }
- rb_bug("Undefined operation in BigDecimalCmp()");
- UNREACHABLE;
- }
- /* Returns True if the value is zero. */
- static VALUE
- BigDecimal_zero(VALUE self)
- {
- Real *a = GetVpValue(self, 1);
- return VpIsZero(a) ? Qtrue : Qfalse;
- }
- /* Returns self if the value is non-zero, nil otherwise. */
- static VALUE
- BigDecimal_nonzero(VALUE self)
- {
- Real *a = GetVpValue(self, 1);
- return VpIsZero(a) ? Qnil : self;
- }
- /* The comparison operator.
- * a <=> b is 0 if a == b, 1 if a > b, -1 if a < b.
- */
- static VALUE
- BigDecimal_comp(VALUE self, VALUE r)
- {
- return BigDecimalCmp(self, r, '*');
- }
- /*
- * Tests for value equality; returns true if the values are equal.
- *
- * The == and === operators and the eql? method have the same implementation
- * for BigDecimal.
- *
- * Values may be coerced to perform the comparison:
- *
- * BigDecimal('1.0') == 1.0 #=> true
- */
- static VALUE
- BigDecimal_eq(VALUE self, VALUE r)
- {
- return BigDecimalCmp(self, r, '=');
- }
- /* call-seq:
- * a < b
- *
- * Returns true if a is less than b.
- *
- * Values may be coerced to perform the comparison (see ==, BigDecimal#coerce).
- */
- static VALUE
- BigDecimal_lt(VALUE self, VALUE r)
- {
- return BigDecimalCmp(self, r, '<');
- }
- /* call-seq:
- * a <= b
- *
- * Returns true if a is less than or equal to b.
- *
- * Values may be coerced to perform the comparison (see ==, BigDecimal#coerce).
- */
- static VALUE
- BigDecimal_le(VALUE self, VALUE r)
- {
- return BigDecimalCmp(self, r, 'L');
- }
- /* call-seq:
- * a > b
- *
- * Returns true if a is greater than b.
- *
- * Values may be coerced to perform the comparison (see ==, BigDecimal#coerce).
- */
- static VALUE
- BigDecimal_gt(VALUE self, VALUE r)
- {
- return BigDecimalCmp(self, r, '>');
- }
- /* call-seq:
- * a >= b
- *
- * Returns true if a is greater than or equal to b.
- *
- * Values may be coerced to perform the comparison (see ==, BigDecimal#coerce)
- */
- static VALUE
- BigDecimal_ge(VALUE self, VALUE r)
- {
- return BigDecimalCmp(self, r, 'G');
- }
- /*
- * call-seq:
- * -big_decimal -> big_decimal
- *
- * Return the negation of self.
- *
- * -BigDecimal('5') #=> -0.5e1
- */
- static VALUE
- BigDecimal_neg(VALUE self)
- {
- ENTER(5);
- Real *c, *a;
- GUARD_OBJ(a, GetVpValue(self, 1));
- GUARD_OBJ(c, VpCreateRbObject(a->Prec *(VpBaseFig() + 1), "0", true));
- VpAsgn(c, a, -1);
- return VpCheckGetValue(c);
- }
- /*
- * Document-method: BigDecimal#mult
- *
- * call-seq: mult(value, digits)
- *
- * Multiply by the specified value.
- *
- * e.g.
- * c = a.mult(b,n)
- * c = a * b
- *
- * digits:: If specified and less than the number of significant digits of the
- * result, the result is rounded to that number of digits, according
- * to BigDecimal.mode.
- */
- static VALUE
- BigDecimal_mult(VALUE self, VALUE r)
- {
- ENTER(5);
- Real *c, *a, *b;
- size_t mx;
- GUARD_OBJ(a, GetVpValue(self, 1));
- if (RB_TYPE_P(r, T_FLOAT)) {
- b = GetVpValueWithPrec(r, 0, 1);
- }
- else if (RB_TYPE_P(r, T_RATIONAL)) {
- b = GetVpValueWithPrec(r, a->Prec*VpBaseFig(), 1);
- }
- else {
- b = GetVpValue(r,0);
- }
- if (!b) return DoSomeOne(self, r, '*');
- SAVE(b);
- mx = a->Prec + b->Prec;
- GUARD_OBJ(c, VpCreateRbObject(mx *(VpBaseFig() + 1), "0", true));
- VpMult(c, a, b);
- return VpCheckGetValue(c);
- }
- static VALUE
- BigDecimal_divide(VALUE self, VALUE r, Real **c, Real **res, Real **div)
- /* For c = self.div(r): with round operation */
- {
- ENTER(5);
- Real *a, *b;
- size_t mx;
- TypedData_Get_Struct(self, Real, &BigDecimal_data_type, a);
- SAVE(a);
- VALUE rr = r;
- if (is_kind_of_BigDecimal(rr)) {
- /* do nothing */
- }
- else if (RB_INTEGER_TYPE_P(r)) {
- rr = rb_inum_convert_to_BigDecimal(r, 0, true);
- }
- else if (RB_TYPE_P(r, T_FLOAT)) {
- rr = rb_float_convert_to_BigDecimal(r, 0, true);
- }
- else if (RB_TYPE_P(r, T_RATIONAL)) {
- rr = rb_rational_convert_to_BigDecimal(r, a->Prec*BASE_FIG, true);
- }
- if (!is_kind_of_BigDecimal(rr)) {
- return DoSomeOne(self, r, '/');
- }
- TypedData_Get_Struct(rr, Real, &BigDecimal_data_type, b);
- SAVE(b);
- *div = b;
- mx = a->Prec + vabs(a->exponent);
- if (mx < b->Prec + vabs(b->exponent)) mx = b->Prec + vabs(b->exponent);
- mx++; /* NOTE: An additional digit is needed for the compatibility to
- the version 1.2.1 and the former. */
- mx = (mx + 1) * VpBaseFig();
- GUARD_OBJ((*c), VpCreateRbObject(mx, "#0", true));
- GUARD_OBJ((*res), VpCreateRbObject((mx+1) * 2 +(VpBaseFig() + 1), "#0", true));
- VpDivd(*c, *res, a, b);
- return Qnil;
- }
- /* call-seq:
- * a / b -> bigdecimal
- * quo(value) -> bigdecimal
- *
- * Divide by the specified value.
- *
- * See BigDecimal#div.
- */
- static VALUE
- BigDecimal_div(VALUE self, VALUE r)
- /* For c = self/r: with round operation */
- {
- ENTER(5);
- Real *c=NULL, *res=NULL, *div = NULL;
- r = BigDecimal_divide(self, r, &c, &res, &div);
- if (!NIL_P(r)) return r; /* coerced by other */
- SAVE(c); SAVE(res); SAVE(div);
- /* a/b = c + r/b */
- /* c xxxxx
- r 00000yyyyy ==> (y/b)*BASE >= HALF_BASE
- */
- /* Round */
- if (VpHasVal(div)) { /* frac[0] must be zero for NaN,INF,Zero */
- VpInternalRound(c, 0, c->frac[c->Prec-1], (DECDIG)(VpBaseVal() * (DECDIG_DBL)res->frac[0] / div->frac[0]));
- }
- return VpCheckGetValue(c);
- }
- /*
- * %: mod = a%b = a - (a.to_f/b).floor * b
- * div = (a.to_f/b).floor
- */
- static VALUE
- BigDecimal_DoDivmod(VALUE self, VALUE r, Real **div, Real **mod)
- {
- ENTER(8);
- Real *c=NULL, *d=NULL, *res=NULL;
- Real *a, *b;
- size_t mx;
- TypedData_Get_Struct(self, Real, &BigDecimal_data_type, a);
- SAVE(a);
- VALUE rr = r;
- if (is_kind_of_BigDecimal(rr)) {
- /* do nothing */
- }
- else if (RB_INTEGER_TYPE_P(r)) {
- rr = rb_inum_convert_to_BigDecimal(r, 0, true);
- }
- else if (RB_TYPE_P(r, T_FLOAT)) {
- rr = rb_float_convert_to_BigDecimal(r, 0, true);
- }
- else if (RB_TYPE_P(r, T_RATIONAL)) {
- rr = rb_rational_convert_to_BigDecimal(r, a->Prec*BASE_FIG, true);
- }
- if (!is_kind_of_BigDecimal(rr)) {
- return Qfalse;
- }
- TypedData_Get_Struct(rr, Real, &BigDecimal_data_type, b);
- SAVE(b);
- if (VpIsNaN(a) || VpIsNaN(b)) goto NaN;
- if (VpIsInf(a) && VpIsInf(b)) goto NaN;
- if (VpIsZero(b)) {
- rb_raise(rb_eZeroDivError, "divided by 0");
- }
- if (VpIsInf(a)) {
- if (VpGetSign(a) == VpGetSign(b)) {
- VALUE inf = BigDecimal_positive_infinity();
- TypedData_Get_Struct(inf, Real, &BigDecimal_data_type, *div);
- }
- else {
- VALUE inf = BigDecimal_negative_infinity();
- TypedData_Get_Struct(inf, Real, &BigDecimal_data_type, *div);
- }
- VALUE nan = BigDecimal_nan();
- TypedData_Get_Struct(nan, Real, &BigDecimal_data_type, *mod);
- return Qtrue;
- }
- if (VpIsInf(b)) {
- VALUE zero = BigDecimal_positive_zero();
- TypedData_Get_Struct(zero, Real, &BigDecimal_data_type, *div);
- *mod = a;
- return Qtrue;
- }
- if (VpIsZero(a)) {
- VALUE zero = BigDecimal_positive_zero();
- TypedData_Get_Struct(zero, Real, &BigDecimal_data_type, *div);
- TypedData_Get_Struct(zero, Real, &BigDecimal_data_type, *mod);
- return Qtrue;
- }
- mx = a->Prec + vabs(a->exponent);
- if (mx<b->Prec + vabs(b->exponent)) mx = b->Prec + vabs(b->exponent);
- mx = (mx + 1) * VpBaseFig();
- GUARD_OBJ(c, VpCreateRbObject(mx, "0", true));
- GUARD_OBJ(res, VpCreateRbObject((mx+1) * 2 +(VpBaseFig() + 1), "#0", true));
- VpDivd(c, res, a, b);
- mx = c->Prec * (VpBaseFig() + 1);
- GUARD_OBJ(d, VpCreateRbObject(mx, "0", true));
- VpActiveRound(d, c, VP_ROUND_DOWN, 0);
- VpMult(res, d, b);
- VpAddSub(c, a, res, -1);
- if (!VpIsZero(c) && (VpGetSign(a) * VpGetSign(b) < 0)) {
- VpAddSub(res, d, VpOne(), -1);
- GUARD_OBJ(d, VpCreateRbObject(GetAddSubPrec(c, b)*(VpBaseFig() + 1), "0", true));
- VpAddSub(d, c, b, 1);
- *div = res;
- *mod = d;
- } else {
- *div = d;
- *mod = c;
- }
- return Qtrue;
- NaN:
- {
- VALUE nan = BigDecimal_nan();
- TypedData_Get_Struct(nan, Real, &BigDecimal_data_type, *div);
- TypedData_Get_Struct(nan, Real, &BigDecimal_data_type, *mod);
- }
- return Qtrue;
- }
- /* call-seq:
- * a % b
- * a.modulo(b)
- *
- * Returns the modulus from dividing by b.
- *
- * See BigDecimal#divmod.
- */
- static VALUE
- BigDecimal_mod(VALUE self, VALUE r) /* %: a%b = a - (a.to_f/b).floor * b */
- {
- ENTER(3);
- Real *div = NULL, *mod = NULL;
- if (BigDecimal_DoDivmod(self, r, &div, &mod)) {
- SAVE(div); SAVE(mod);
- return VpCheckGetValue(mod);
- }
- return DoSomeOne(self, r, '%');
- }
- static VALUE
- BigDecimal_divremain(VALUE self, VALUE r, Real **dv, Real **rv)
- {
- ENTER(10);
- size_t mx;
- Real *a = NULL, *b = NULL, *c = NULL, *res = NULL, *d = NULL, *rr = NULL, *ff = NULL;
- Real *f = NULL;
- GUARD_OBJ(a, GetVpValue(self, 1));
- if (RB_TYPE_P(r, T_FLOAT)) {
- b = GetVpValueWithPrec(r, 0, 1);
- }
- else if (RB_TYPE_P(r, T_RATIONAL)) {
- b = GetVpValueWithPrec(r, a->Prec*VpBaseFig(), 1);
- }
- else {
- b = GetVpValue(r, 0);
- }
- if (!b) return DoSomeOne(self, r, rb_intern("remainder"));
- SAVE(b);
- mx = (a->MaxPrec + b->MaxPrec) *VpBaseFig();
- GUARD_OBJ(c, VpCreateRbObject(mx, "0", true));
- GUARD_OBJ(res, VpCreateRbObject((mx+1) * 2 + (VpBaseFig() + 1), "#0", true));
- GUARD_OBJ(rr, VpCreateRbObject((mx+1) * 2 + (VpBaseFig() + 1), "#0", true));
- GUARD_OBJ(ff, VpCreateRbObject((mx+1) * 2 + (VpBaseFig() + 1), "#0", true));
- VpDivd(c, res, a, b);
- mx = c->Prec *(VpBaseFig() + 1);
- GUARD_OBJ(d, VpCreateRbObject(mx, "0", true));
- GUARD_OBJ(f, VpCreateRbObject(mx, "0", true));
- VpActiveRound(d, c, VP_ROUND_DOWN, 0); /* 0: round off */
- VpFrac(f, c);
- VpMult(rr, f, b);
- VpAddSub(ff, res, rr, 1);
- *dv = d;
- *rv = ff;
- return Qnil;
- }
- /* call-seq:
- * remainder(value)
- *
- * Returns the remainder from dividing by the value.
- *
- * x.remainder(y) means x-y*(x/y).truncate
- */
- static VALUE
- BigDecimal_remainder(VALUE self, VALUE r) /* remainder */
- {
- VALUE f;
- Real *d, *rv = 0;
- f = BigDecimal_divremain(self, r, &d, &rv);
- if (!NIL_P(f)) return f;
- return VpCheckGetValue(rv);
- }
- /* call-seq:
- * divmod(value)
- *
- * Divides by the specified value, and returns the quotient and modulus
- * as BigDecimal numbers. The quotient is rounded towards negative infinity.
- *
- * For example:
- *
- * require 'bigdecimal'
- *
- * a = BigDecimal("42")
- * b = BigDecimal("9")
- *
- * q, m = a.divmod(b)
- *
- * c = q * b + m
- *
- * a == c #=> true
- *
- * The quotient q is (a/b).floor, and the modulus is the amount that must be
- * added to q * b to get a.
- */
- static VALUE
- BigDecimal_divmod(VALUE self, VALUE r)
- {
- ENTER(5);
- Real *div = NULL, *mod = NULL;
- if (BigDecimal_DoDivmod(self, r, &div, &mod)) {
- SAVE(div); SAVE(mod);
- return rb_assoc_new(VpCheckGetValue(div), VpCheckGetValue(mod));
- }
- return DoSomeOne(self,r,rb_intern("divmod"));
- }
- /*
- * Do the same manner as Float#div when n is nil.
- * Do the same manner as BigDecimal#quo when n is 0.
- */
- static inline VALUE
- BigDecimal_div2(VALUE self, VALUE b, VALUE n)
- {
- ENTER(5);
- SIGNED_VALUE ix;
- if (NIL_P(n)) { /* div in Float sense */
- Real *div = NULL;
- Real *mod;
- if (BigDecimal_DoDivmod(self, b, &div, &mod)) {
- return BigDecimal_to_i(VpCheckGetValue(div));
- }
- return DoSomeOne(self, b, rb_intern("div"));
- }
- /* div in BigDecimal sense */
- ix = GetPrecisionInt(n);
- if (ix == 0) {
- return BigDecimal_div(self, b);
- }
- else {
- Real *res = NULL;
- Real *av = NULL, *bv = NULL, *cv = NULL;
- size_t mx = ix + VpBaseFig()*2;
- size_t pl = VpSetPrecLimit(0);
- GUARD_OBJ(cv, VpCreateRbObject(mx + VpBaseFig(), "0", true));
- GUARD_OBJ(av, GetVpValue(self, 1));
- GUARD_OBJ(bv, GetVpValue(b, 1));
- mx = av->Prec + bv->Prec + 2;
- if (mx <= cv->MaxPrec) mx = cv->MaxPrec + 1;
- GUARD_OBJ(res, VpCreateRbObject((mx * 2 + 2)*VpBaseFig(), "#0", true));
- VpDivd(cv, res, av, bv);
- VpSetPrecLimit(pl);
- VpLeftRound(cv, VpGetRoundMode(), ix);
- return VpCheckGetValue(cv);
- }
- }
- /*
- * Document-method: BigDecimal#div
- *
- * call-seq:
- * div(value, digits) -> bigdecimal or integer
- *
- * Divide by the specified value.
- *
- * digits:: If specified and less than the number of significant digits of the
- * result, the result is rounded to that number of digits, according
- * to BigDecimal.mode.
- *
- * If digits is 0, the result is the same as for the / operator
- * or #quo.
- *
- * If digits is not specified, the result is an integer,
- * by analogy with Float#div; see also BigDecimal#divmod.
- *
- * Examples:
- *
- * a = BigDecimal("4")
- * b = BigDecimal("3")
- *
- * a.div(b, 3) # => 0.133e1
- *
- * a.div(b, 0) # => 0.1333333333333333333e1
- * a / b # => 0.1333333333333333333e1
- * a.quo(b) # => 0.1333333333333333333e1
- *
- * a.div(b) # => 1
- */
- static VALUE
- BigDecimal_div3(int argc, VALUE *argv, VALUE self)
- {
- VALUE b,n;
- rb_scan_args(argc, argv, "11", &b, &n);
- return BigDecimal_div2(self, b, n);
- }
- static VALUE
- BigDecimal_add2(VALUE self, VALUE b, VALUE n)
- {
- ENTER(2);
- Real *cv;
- SIGNED_VALUE mx = GetPrecisionInt(n);
- if (mx == 0) return BigDecimal_add(self, b);
- else {
- size_t pl = VpSetPrecLimit(0);
- VALUE c = BigDecimal_add(self, b);
- VpSetPrecLimit(pl);
- GUARD_OBJ(cv, GetVpValue(c, 1));
- VpLeftRound(cv, VpGetRoundMode(), mx);
- return VpCheckGetValue(cv);
- }
- }
- /* call-seq:
- * sub(value, digits) -> bigdecimal
- *
- * Subtract the specified value.
- *
- * e.g.
- * c = a.sub(b,n)
- *
- * digits:: If specified and less than the number of significant digits of the
- * result, the result is rounded to that number of digits, according
- * to BigDecimal.mode.
- *
- */
- static VALUE
- BigDecimal_sub2(VALUE self, VALUE b, VALUE n)
- {
- ENTER(2);
- Real *cv;
- SIGNED_VALUE mx = GetPrecisionInt(n);
- if (mx == 0) return BigDecimal_sub(self, b);
- else {
- size_t pl = VpSetPrecLimit(0);
- VALUE c = BigDecimal_sub(self, b);
- VpSetPrecLimit(pl);
- GUARD_OBJ(cv, GetVpValue(c, 1));
- VpLeftRound(cv, VpGetRoundMode(), mx);
- return VpCheckGetValue(cv);
- }
- }
- static VALUE
- BigDecimal_mult2(VALUE self, VALUE b, VALUE n)
- {
- ENTER(2);
- Real *cv;
- SIGNED_VALUE mx = GetPrecisionInt(n);
- if (mx == 0) return BigDecimal_mult(self, b);
- else {
- size_t pl = VpSetPrecLimit(0);
- VALUE c = BigDecimal_mult(self, b);
- VpSetPrecLimit(pl);
- GUARD_OBJ(cv, GetVpValue(c, 1));
- VpLeftRound(cv, VpGetRoundMode(), mx);
- return VpCheckGetValue(cv);
- }
- }
- /*
- * call-seq:
- * big_decimal.abs -> big_decimal
- *
- * Returns the absolute value, as a BigDecimal.
- *
- * BigDecimal('5').abs #=> 0.5e1
- * BigDecimal('-3').abs #=> 0.3e1
- */
- static VALUE
- BigDecimal_abs(VALUE self)
- {
- ENTER(5);
- Real *c, *a;
- size_t mx;
- GUARD_OBJ(a, GetVpValue(self, 1));
- mx = a->Prec *(VpBaseFig() + 1);
- GUARD_OBJ(c, VpCreateRbObject(mx, "0", true));
- VpAsgn(c, a, 1);
- VpChangeSign(c, 1);
- return VpCheckGetValue(c);
- }
- /* call-seq:
- * sqrt(n)
- *
- * Returns the square root of the value.
- *
- * Result has at least n significant digits.
- */
- static VALUE
- BigDecimal_sqrt(VALUE self, VALUE nFig)
- {
- ENTER(5);
- Real *c, *a;
- size_t mx, n;
- GUARD_OBJ(a, GetVpValue(self, 1));
- mx = a->Prec * (VpBaseFig() + 1);
- n = GetPrecisionInt(nFig) + VpDblFig() + BASE_FIG;
- if (mx <= n) mx = n;
- GUARD_OBJ(c, VpCreateRbObject(mx, "0", true));
- VpSqrt(c, a);
- return VpCheckGetValue(c);
- }
- /* Return the integer part of the number, as a BigDecimal.
- */
- static VALUE
- BigDecimal_fix(VALUE self)
- {
- ENTER(5);
- Real *c, *a;
- size_t mx;
- GUARD_OBJ(a, GetVpValue(self, 1));
- mx = a->Prec *(VpBaseFig() + 1);
- GUARD_OBJ(c, VpCreateRbObject(mx, "0", true));
- VpActiveRound(c, a, VP_ROUND_DOWN, 0); /* 0: round off */
- return VpCheckGetValue(c);
- }
- /* call-seq:
- * round(n, mode)
- *
- * Round to the nearest integer (by default), returning the result as a
- * BigDecimal if n is specified, or as an Integer if it isn't.
- *
- * BigDecimal('3.14159').round #=> 3
- * BigDecimal('8.7').round #=> 9
- * BigDecimal('-9.9').round #=> -10
- *
- * BigDecimal('3.14159').round(2).class.name #=> "BigDecimal"
- * BigDecimal('3.14159').round.class.name #=> "Integer"
- *
- * If n is specified and positive, the fractional part of the result has no
- * more than that many digits.
- *
- * If n is specified and negative, at least that many digits to the left of the
- * decimal point will be 0 in the result, and return value will be an Integer.
- *
- * BigDecimal('3.14159').round(3) #=> 3.142
- * BigDecimal('13345.234').round(-2) #=> 13300
- *
- * The value of the optional mode argument can be used to determine how
- * rounding is performed; see BigDecimal.mode.
- */
- static VALUE
- BigDecimal_round(int argc, VALUE *argv, VALUE self)
- {
- ENTER(5);
- Real *c, *a;
- int iLoc = 0;
- VALUE vLoc;
- VALUE vRound;
- int round_to_int = 0;
- size_t mx, pl;
- unsigned short sw = VpGetRoundMode();
- switch (rb_scan_args(argc, argv, "02", &vLoc, &vRound)) {
- case 0:
- iLoc = 0;
- round_to_int = 1;
- break;
- case 1:
- if (RB_TYPE_P(vLoc, T_HASH)) {
- sw = check_rounding_mode_option(vLoc);
- }
- else {
- iLoc = NUM2INT(vLoc);
- if (iLoc < 1) round_to_int = 1;
- }
- break;
- case 2:
- iLoc = NUM2INT(vLoc);
- if (RB_TYPE_P(vRound, T_HASH)) {
- sw = check_rounding_mode_option(vRound);
- }
- else {
- sw = check_rounding_mode(vRound);
- }
- break;
- default:
- break;
- }
- pl = VpSetPrecLimit(0);
- GUARD_OBJ(a, GetVpValue(self, 1));
- mx = a->Prec * (VpBaseFig() + 1);
- GUARD_OBJ(c, VpCreateRbObject(mx, "0", true));
- VpSetPrecLimit(pl);
- VpActiveRound(c, a, sw, iLoc);
- if (round_to_int) {
- return BigDecimal_to_i(VpCheckGetValue(c));
- }
- return VpCheckGetValue(c);
- }
- /* call-seq:
- * truncate(n)
- *
- * Truncate to the nearest integer (by default), returning the result as a
- * BigDecimal.
- *
- * BigDecimal('3.14159').truncate #=> 3
- * BigDecimal('8.7').truncate #=> 8
- * BigDecimal('-9.9').truncate #=> -9
- *
- * If n is specified and positive, the fractional part of the result has no
- * more than that many digits.
- *
- * If n is specified and negative, at least that many digits to the left of the
- * decimal point will be 0 in the result.
- *
- * BigDecimal('3.14159').truncate(3) #=> 3.141
- * BigDecimal('13345.234').truncate(-2) #=> 13300.0
- */
- static VALUE
- BigDecimal_truncate(int argc, VALUE *argv, VALUE self)
- {
- ENTER(5);
- Real *c, *a;
- int iLoc;
- VALUE vLoc;
- size_t mx, pl = VpSetPrecLimit(0);
- if (rb_scan_args(argc, argv, "01", &vLoc) == 0) {
- iLoc = 0;
- }
- else {
- iLoc = NUM2INT(vLoc);
- }
- GUARD_OBJ(a, GetVpValue(self, 1));
- mx = a->Prec * (VpBaseFig() + 1);
- GUARD_OBJ(c, VpCreateRbObject(mx, "0", true));
- VpSetPrecLimit(pl);
- VpActiveRound(c, a, VP_ROUND_DOWN, iLoc); /* 0: truncate */
- if (argc == 0) {
- return BigDecimal_to_i(VpCheckGetValue(c));
- }
- return VpCheckGetValue(c);
- }
- /* Return the fractional part of the number, as a BigDecimal.
- */
- static VALUE
- BigDecimal_frac(VALUE self)
- {
- ENTER(5);
- Real *c, *a;
- size_t mx;
- GUARD_OBJ(a, GetVpValue(self, 1));
- mx = a->Prec * (VpBaseFig() + 1);
- GUARD_OBJ(c, VpCreateRbObject(mx, "0", true));
- VpFrac(c, a);
- return VpCheckGetValue(c);
- }
- /* call-seq:
- * floor(n)
- *
- * Return the largest integer less than or equal to the value, as a BigDecimal.
- *
- * BigDecimal('3.14159').floor #=> 3
- * BigDecimal('-9.1').floor #=> -10
- *
- * If n is specified and positive, the fractional part of the result has no
- * more than that many digits.
- *
- * If n is specified and negative, at least that
- * many digits to the left of the decimal point will be 0 in the result.
- *
- * BigDecimal('3.14159').floor(3) #=> 3.141
- * BigDecimal('13345.234').floor(-2) #=> 13300.0
- */
- static VALUE
- BigDecimal_floor(int argc, VALUE *argv, VALUE self)
- {
- ENTER(5);
- Real *c, *a;
- int iLoc;
- VALUE vLoc;
- size_t mx, pl = VpSetPrecLimit(0);
- if (rb_scan_args(argc, argv, "01", &vLoc)==0) {
- iLoc = 0;
- }
- else {
- iLoc = NUM2INT(vLoc);
- }
- GUARD_OBJ(a, GetVpValue(self, 1));
- mx = a->Prec * (VpBaseFig() + 1);
- GUARD_OBJ(c, VpCreateRbObject(mx, "0", true));
- VpSetPrecLimit(pl);
- VpActiveRound(c, a, VP_ROUND_FLOOR, iLoc);
- #ifdef BIGDECIMAL_DEBUG
- VPrint(stderr, "floor: c=%\n", c);
- #endif
- if (argc == 0) {
- return Bi…
Large files files are truncated, but you can click here to view the full file