/ext/bigdecimal/bigdecimal.c
C | 5053 lines | 3569 code | 423 blank | 1061 comment | 737 complexity | a241cd5abe75ccc357df5e107464434b MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause, AGPL-3.0, 0BSD, Unlicense
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)
- *
- * You may distribute under the terms of either the GNU General Public
- * License or the Artistic License, as specified in the README file
- * of this BigDecimal distribution.
- *
- * NOTE: Change log in this source removed to reduce source code size.
- * See rev. 1.25 if needed.
- *
- */
- /* #define BIGDECIMAL_DEBUG 1 */
- #include "bigdecimal.h"
- #include <ctype.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <errno.h>
- #include <math.h>
- #include "math.h"
- #ifdef HAVE_IEEEFP_H
- #include <ieeefp.h>
- #endif
- /* #define ENABLE_NUMERIC_STRING */
- VALUE rb_cBigDecimal;
- 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;
- /* MACRO's to guard objects from GC by keeping them in stack */
- #define ENTER(n) volatile VALUE vStack[n];int iStack=0
- #define PUSH(x) vStack[iStack++] = (unsigned long)(x);
- #define SAVE(p) PUSH(p->obj);
- #define GUARD_OBJ(p,y) {p=y;SAVE(p);}
- #define BASE_FIG RMPD_COMPONENT_FIGURES
- #define BASE RMPD_BASE
- #define HALF_BASE (BASE/2)
- #define BASE1 (BASE/10)
- #ifndef DBLE_FIG
- #define DBLE_FIG (DBL_DIG+1) /* figure of double */
- #endif
- /*
- * ================== Ruby Interface part ==========================
- */
- #define DoSomeOne(x,y,f) rb_num_coerce_bin(x,y,f)
- /*
- * Returns the BigDecimal version number.
- *
- * Ruby 1.8.0 returns 1.0.0.
- * Ruby 1.8.1 thru 1.8.3 return 1.0.1.
- */
- static VALUE
- BigDecimal_version(VALUE self)
- {
- /*
- * 1.0.0: Ruby 1.8.0
- * 1.0.1: Ruby 1.8.1
- */
- return rb_str_new2("1.0.1");
- }
- /*
- * 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, BDIGIT vPrev, BDIGIT v);
- static int VpLimitRound(Real *c, size_t ixDigit);
- /*
- * **** BigDecimal part ****
- */
- static void
- BigDecimal_delete(void *pv)
- {
- VpFree(pv);
- }
- static size_t
- BigDecimal_memsize(const void *ptr)
- {
- const Real *pv = ptr;
- return pv ? (sizeof(*pv) + pv->MaxPrec * sizeof(BDIGIT)) : 0;
- }
- static const rb_data_type_t BigDecimal_data_type = {
- "BigDecimal",
- {0, BigDecimal_delete, BigDecimal_memsize,},
- };
- static VALUE
- ToValue(Real *p)
- {
- if(VpIsNaN(p)) {
- VpException(VP_EXCEPTION_NaN,"Computation results to 'NaN'(Not a Number)",0);
- } else if(VpIsPosInf(p)) {
- VpException(VP_EXCEPTION_INFINITY,"Computation results to 'Infinity'",0);
- } else if(VpIsNegInf(p)) {
- VpException(VP_EXCEPTION_INFINITY,"Computation results to '-Infinity'",0);
- }
- return p->obj;
- }
- static Real *
- GetVpValue(VALUE v, int must)
- {
- Real *pv;
- VALUE bg;
- char szD[128];
- VALUE orig = Qundef;
- int util_loaded = 0;
- again:
- switch(TYPE(v))
- {
- case T_RATIONAL:
- if(orig == Qundef ? (orig = v, 1) : orig != v) {
- if(!util_loaded) {
- rb_require("bigdecimal/util");
- util_loaded = 1;
- }
- v = rb_funcall2(v, rb_intern("to_d"), 0, 0);
- goto again;
- }
- v = orig;
- goto SomeOneMayDoIt;
- case T_DATA:
- if(rb_typeddata_is_kind_of(v, &BigDecimal_data_type)) {
- pv = DATA_PTR(v);
- return pv;
- } else {
- goto SomeOneMayDoIt;
- }
- break;
- case T_FIXNUM:
- sprintf(szD, "%ld", FIX2LONG(v));
- return VpCreateRbObject(VpBaseFig() * 2 + 1, szD);
- #ifdef ENABLE_NUMERIC_STRING
- case T_STRING:
- SafeStringValue(v);
- return VpCreateRbObject(strlen(RSTRING_PTR(v)) + VpBaseFig() + 1,
- RSTRING_PTR(v));
- #endif /* ENABLE_NUMERIC_STRING */
- case T_BIGNUM:
- bg = rb_big2str(v, 10);
- return VpCreateRbObject(strlen(RSTRING_PTR(bg)) + VpBaseFig() + 1,
- RSTRING_PTR(bg));
- default:
- goto SomeOneMayDoIt;
- }
- SomeOneMayDoIt:
- if(must) {
- rb_raise(rb_eTypeError, "%s can't be coerced into BigDecimal",
- rb_special_const_p(v)?
- RSTRING_PTR(rb_inspect(v)):
- rb_obj_classname(v)
- );
- }
- return NULL; /* NULL means to coerce */
- }
- /* 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:
- * precs
- *
- * Returns an Array of two Integer values.
- *
- * The first value is the current number of significant digits in the
- * BigDecimal. The second value is the maximum number of significant digits
- * for the BigDecimal.
- */
- static VALUE
- BigDecimal_prec(VALUE self)
- {
- ENTER(1);
- Real *p;
- VALUE obj;
- GUARD_OBJ(p,GetVpValue(self,1));
- obj = rb_assoc_new(INT2NUM(p->Prec*VpBaseFig()),
- INT2NUM(p->MaxPrec*VpBaseFig()));
- return obj;
- }
- 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(BDIGIT)*p->Prec);
- hash += p->exponent;
- }
- return INT2FIX(hash);
- }
- 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;
- SafeStringValue(str);
- pch = (unsigned char *)RSTRING_PTR(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));
- m /= VpBaseFig();
- if(m && pv->MaxPrec>m) pv->MaxPrec = m+1;
- return ToValue(pv);
- }
- 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;
- }
- Check_Type(v, T_FIXNUM);
- sw = (unsigned short)FIX2UINT(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;
- if(rb_scan_args(argc,argv,"11",&which,&val)==1) val = Qnil;
- Check_Type(which, T_FIXNUM);
- f = (unsigned long)FIX2INT(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
- GetPositiveInt(VALUE v)
- {
- SIGNED_VALUE n;
- Check_Type(v, T_FIXNUM);
- n = FIX2INT(v);
- if (n < 0) {
- rb_raise(rb_eArgError, "argument must be positive");
- }
- return n;
- }
- VP_EXPORT Real *
- VpNewRbClass(size_t mx, char *str, VALUE klass)
- {
- Real *pv = VpAlloc(mx,str);
- pv->obj = TypedData_Wrap_Struct(klass, &BigDecimal_data_type, pv);
- return pv;
- }
- VP_EXPORT Real *
- VpCreateRbObject(size_t mx, const char *str)
- {
- Real *pv = VpAlloc(mx,str);
- pv->obj = TypedData_Wrap_Struct(rb_cBigDecimal, &BigDecimal_data_type, pv);
- 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)
- {
- if(VpIsNaN(p)) {
- VpException(VP_EXCEPTION_NaN,"Computation results to 'NaN'(Not a Number)",1);
- } else if(VpIsPosInf(p)) {
- VpException(VP_EXCEPTION_INFINITY,"Computation results to 'Infinity'",1);
- } else if(VpIsNegInf(p)) {
- VpException(VP_EXCEPTION_INFINITY,"Computation results to '-Infinity'",1);
- }
- }
- static VALUE BigDecimal_split(VALUE self);
- /* Returns the value as an integer (Fixnum or Bignum).
- *
- * If the BigNumber 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)*(BDIGIT_DBL_SIGNED)p->frac[0]));
- }
- else {
- VALUE a = BigDecimal_split(self);
- VALUE digits = RARRAY_PTR(a)[1];
- VALUE numerator = rb_funcall(digits, rb_intern("to_i"), 0);
- ssize_t dpower = e - (ssize_t)RSTRING_LEN(digits);
- if (VpGetSign(p) < 0) {
- numerator = rb_funcall(numerator, '*', 1, INT2FIX(-1));
- }
- if (dpower < 0) {
- return rb_funcall(numerator, rb_intern("div"), 1,
- rb_funcall(INT2FIX(10), rb_intern("**"), 1,
- INT2FIX(-dpower)));
- }
- return rb_funcall(numerator, '*', 1,
- rb_funcall(INT2FIX(10), rb_intern("**"), 1,
- INT2FIX(dpower)));
- }
- }
- /* 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)
- goto overflow;
- return rb_float_new(d);
- overflow:
- VpException(VP_EXCEPTION_OVERFLOW, "BigDecimal to Float conversion", 0);
- if (d > 0.0)
- return rb_float_new(VpGetDoublePosInf());
- else
- return rb_float_new(VpGetDoubleNegInf());
- underflow:
- VpException(VP_EXCEPTION_UNDERFLOW, "BigDecimal to Float conversion", 0);
- if (d > 0.0)
- 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_PTR(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.new("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 (TYPE(other) == T_FLOAT) {
- obj = rb_assoc_new(other, BigDecimal_to_f(self));
- } else {
- GUARD_OBJ(b,GetVpValue(other,1));
- obj = rb_assoc_new(b->obj, self);
- }
- return obj;
- }
- static VALUE
- BigDecimal_uplus(VALUE self)
- {
- return self;
- }
- /* 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));
- 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"));
- VpAddSub(c, a, b, 1);
- } else {
- GUARD_OBJ(c,VpCreateRbObject(mx *(VpBaseFig() + 1), "0"));
- if(!mx) {
- VpSetInf(c,VpGetSign(a));
- } else {
- VpAddSub(c, a, b, 1);
- }
- }
- return ToValue(c);
- }
- /* call-seq:
- * sub(value, digits)
- *
- * Subtract the specified value.
- *
- * e.g.
- * c = a.sub(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_sub(VALUE self, VALUE r)
- {
- ENTER(5);
- Real *c, *a, *b;
- size_t mx;
- GUARD_OBJ(a,GetVpValue(self,1));
- 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"));
- VpAddSub(c, a, b, -1);
- } else {
- GUARD_OBJ(c,VpCreateRbObject(mx *(VpBaseFig() + 1), "0"));
- if(!mx) {
- VpSetInf(c,VpGetSign(a));
- } else {
- VpAddSub(c, a, b, -1);
- }
- }
- return ToValue(c);
- }
- static VALUE
- BigDecimalCmp(VALUE self, VALUE r,char op)
- {
- ENTER(5);
- SIGNED_VALUE e;
- Real *a, *b;
- GUARD_OBJ(a,GetVpValue(self,1));
- b = GetVpValue(r,0);
- if(!b) {
- 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 '>': case '<': f = (ID)op; 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;
- }
- rb_bug("Undefined operation in BigDecimalCmp()");
- }
- /* 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.new('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 ==, 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 ==, 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 ==, 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 ==, coerce)
- */
- static VALUE
- BigDecimal_ge(VALUE self, VALUE r)
- {
- return BigDecimalCmp(self, r, 'G');
- }
- 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"));
- VpAsgn(c, a, -1);
- return ToValue(c);
- }
- /* 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));
- 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"));
- VpMult(c, a, b);
- return ToValue(c);
- }
- static VALUE
- BigDecimal_divide(Real **c, Real **res, Real **div, VALUE self, VALUE r)
- /* For c = self.div(r): with round operation */
- {
- ENTER(5);
- Real *a, *b;
- size_t mx;
- GUARD_OBJ(a,GetVpValue(self,1));
- b = GetVpValue(r,0);
- if(!b) return DoSomeOne(self,r,'/');
- SAVE(b);
- *div = b;
- 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"));
- GUARD_OBJ((*res),VpCreateRbObject((mx+1) * 2 +(VpBaseFig() + 1), "#0"));
- VpDivd(*c, *res, a, b);
- return (VALUE)0;
- }
- /* call-seq:
- * div(value, digits)
- * quo(value)
- *
- * Divide by the specified value.
- *
- * e.g.
- * c = a.div(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.
- *
- * If digits is 0, the result is the same as the / operator. If not, the
- * result is an integer BigDecimal, by analogy with Float#div.
- *
- * The alias quo is provided since div(value, 0) is the same as computing
- * the quotient; see divmod.
- */
- 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(&c, &res, &div, self, r);
- if(r!=(VALUE)0) 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], (BDIGIT)(VpBaseVal()*(BDIGIT_DBL)res->frac[0]/div->frac[0]));
- }
- return ToValue(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;
- GUARD_OBJ(a,GetVpValue(self,1));
- b = GetVpValue(r,0);
- if(!b) return Qfalse;
- 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)) {
- GUARD_OBJ(d,VpCreateRbObject(1, "0"));
- VpSetInf(d, (SIGNED_VALUE)(VpGetSign(a) == VpGetSign(b) ? 1 : -1));
- GUARD_OBJ(c,VpCreateRbObject(1, "NaN"));
- *div = d;
- *mod = c;
- return Qtrue;
- }
- if(VpIsInf(b)) {
- GUARD_OBJ(d,VpCreateRbObject(1, "0"));
- *div = d;
- *mod = a;
- return Qtrue;
- }
- if(VpIsZero(a)) {
- GUARD_OBJ(c,VpCreateRbObject(1, "0"));
- GUARD_OBJ(d,VpCreateRbObject(1, "0"));
- *div = d;
- *mod = c;
- 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"));
- GUARD_OBJ(res,VpCreateRbObject((mx+1) * 2 +(VpBaseFig() + 1), "#0"));
- VpDivd(c, res, a, b);
- mx = c->Prec *(VpBaseFig() + 1);
- GUARD_OBJ(d,VpCreateRbObject(mx, "0"));
- 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"));
- VpAddSub(d ,c,b, 1);
- *div = res;
- *mod = d;
- } else {
- *div = d;
- *mod = c;
- }
- return Qtrue;
- NaN:
- GUARD_OBJ(c,VpCreateRbObject(1, "NaN"));
- GUARD_OBJ(d,VpCreateRbObject(1, "NaN"));
- *div = d;
- *mod = c;
- return Qtrue;
- }
- /* call-seq:
- * a % b
- * a.modulo(b)
- *
- * Returns the modulus from dividing by b. See 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 ToValue(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));
- 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"));
- GUARD_OBJ(res,VpCreateRbObject((mx+1) * 2 +(VpBaseFig() + 1), "#0"));
- GUARD_OBJ(rr ,VpCreateRbObject((mx+1) * 2 +(VpBaseFig() + 1), "#0"));
- GUARD_OBJ(ff ,VpCreateRbObject((mx+1) * 2 +(VpBaseFig() + 1), "#0"));
- VpDivd(c, res, a, b);
- mx = c->Prec *(VpBaseFig() + 1);
- GUARD_OBJ(d,VpCreateRbObject(mx, "0"));
- GUARD_OBJ(f,VpCreateRbObject(mx, "0"));
- 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 (VALUE)0;
- }
- /* 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(f!=(VALUE)0) return f;
- return ToValue(rv);
- }
- /* 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.new("42")
- * b = BigDecimal.new("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(ToValue(div), ToValue(mod));
- }
- return DoSomeOne(self,r,rb_intern("divmod"));
- }
- static VALUE
- BigDecimal_div2(int argc, VALUE *argv, VALUE self)
- {
- ENTER(5);
- VALUE b,n;
- int na = rb_scan_args(argc,argv,"11",&b,&n);
- if(na==1) { /* div in Float sense */
- Real *div=NULL;
- Real *mod;
- if(BigDecimal_DoDivmod(self,b,&div,&mod)) {
- return BigDecimal_to_i(ToValue(div));
- }
- return DoSomeOne(self,b,rb_intern("div"));
- } else { /* div in BigDecimal sense */
- SIGNED_VALUE ix = GetPositiveInt(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,"0"));
- 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"));
- VpDivd(cv,res,av,bv);
- VpSetPrecLimit(pl);
- VpLeftRound(cv,(int)VpGetRoundMode(),ix);
- return ToValue(cv);
- }
- }
- }
- static VALUE
- BigDecimal_add2(VALUE self, VALUE b, VALUE n)
- {
- ENTER(2);
- Real *cv;
- SIGNED_VALUE mx = GetPositiveInt(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,(int)VpGetRoundMode(),mx);
- return ToValue(cv);
- }
- }
- static VALUE
- BigDecimal_sub2(VALUE self, VALUE b, VALUE n)
- {
- ENTER(2);
- Real *cv;
- SIGNED_VALUE mx = GetPositiveInt(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,(int)VpGetRoundMode(),mx);
- return ToValue(cv);
- }
- }
- static VALUE
- BigDecimal_mult2(VALUE self, VALUE b, VALUE n)
- {
- ENTER(2);
- Real *cv;
- SIGNED_VALUE mx = GetPositiveInt(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,(int)VpGetRoundMode(),mx);
- return ToValue(cv);
- }
- }
- /* Returns the absolute value.
- *
- * BigDecimal('5').abs -> 5
- *
- * BigDecimal('-3').abs -> 3
- */
- 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"));
- VpAsgn(c, a, 1);
- VpChangeSign(c, 1);
- return ToValue(c);
- }
- /* call-seq:
- * sqrt(n)
- *
- * Returns the square root of the value.
- *
- * If n is specified, returns at least that many 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 = GetPositiveInt(nFig) + VpDblFig() + 1;
- if(mx <= n) mx = n;
- GUARD_OBJ(c,VpCreateRbObject(mx, "0"));
- VpSqrt(c, a);
- return ToValue(c);
- }
- /* Return the integer part of the number.
- */
- 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"));
- VpActiveRound(c,a,VP_ROUND_DOWN,0); /* 0: round off */
- return ToValue(c);
- }
- /* call-seq:
- * round(n, mode)
- *
- * Round to the nearest 1 (by default), returning the result as a BigDecimal.
- *
- * BigDecimal('3.14159').round -> 3
- *
- * BigDecimal('8.7').round -> 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').round(3) -> 3.142
- *
- * BigDecimal('13345.234').round(-2) -> 13300.0
- *
- * 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;
- size_t mx, pl;
- unsigned short sw = VpGetRoundMode();
- switch (rb_scan_args(argc, argv, "02", &vLoc, &vRound)) {
- case 0:
- iLoc = 0;
- break;
- case 1:
- Check_Type(vLoc, T_FIXNUM);
- iLoc = FIX2INT(vLoc);
- break;
- case 2:
- Check_Type(vLoc, T_FIXNUM);
- iLoc = FIX2INT(vLoc);
- sw = check_rounding_mode(vRound);
- break;
- }
- pl = VpSetPrecLimit(0);
- GUARD_OBJ(a,GetVpValue(self,1));
- mx = a->Prec *(VpBaseFig() + 1);
- GUARD_OBJ(c,VpCreateRbObject(mx, "0"));
- VpSetPrecLimit(pl);
- VpActiveRound(c,a,sw,iLoc);
- if (argc == 0) {
- return BigDecimal_to_i(ToValue(c));
- }
- return ToValue(c);
- }
- /* call-seq:
- * truncate(n)
- *
- * Truncate to the nearest 1, returning the result as a BigDecimal.
- *
- * BigDecimal('3.14159').truncate -> 3
- *
- * BigDecimal('8.7').truncate -> 8
- *
- * 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 {
- Check_Type(vLoc, T_FIXNUM);
- iLoc = FIX2INT(vLoc);
- }
- GUARD_OBJ(a,GetVpValue(self,1));
- mx = a->Prec *(VpBaseFig() + 1);
- GUARD_OBJ(c,VpCreateRbObject(mx, "0"));
- VpSetPrecLimit(pl);
- VpActiveRound(c,a,VP_ROUND_DOWN,iLoc); /* 0: truncate */
- if (argc == 0) {
- return BigDecimal_to_i(ToValue(c));
- }
- return ToValue(c);
- }
- /* Return the fractional part of the number.
- */
- 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"));
- VpFrac(c, a);
- return ToValue(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 {
- Check_Type(vLoc, T_FIXNUM);
- iLoc = FIX2INT(vLoc);
- }
- GUARD_OBJ(a,GetVpValue(self,1));
- mx = a->Prec *(VpBaseFig() + 1);
- GUARD_OBJ(c,VpCreateRbObject(mx, "0"));
- VpSetPrecLimit(pl);
- VpActiveRound(c,a,VP_ROUND_FLOOR,iLoc);
- #ifdef BIGDECIMAL_DEBUG
- VPrint(stderr, "floor: c=%\n", c);
- #endif
- if (argc == 0) {
- return BigDecimal_to_i(ToValue(c));
- }
- return ToValue(c);
- }
- /* call-seq:
- * ceil(n)
- *
- * Return the smallest integer greater than or equal to the value, as a BigDecimal.
- *
- * BigDecimal('3.14159').ceil -> 4
- *
- * BigDecimal('-9.1').ceil -> -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').ceil(3) -> 3.142
- *
- * BigDecimal('13345.234').ceil(-2) -> 13400.0
- */
- static VALUE
- BigDecimal_ceil(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 {
- Check_Type(vLoc, T_FIXNUM);
- iLoc = FIX2INT(vLoc);
- }
- GUARD_OBJ(a,GetVpValue(self,1));
- mx = a->Prec *(VpBaseFig() + 1);
- GUARD_OBJ(c,VpCreateRbObject(mx, "0"));
- VpSetPrecLimit(pl);
- VpActiveRound(c,a,VP_ROUND_CEIL,iLoc);
- if (argc == 0) {
- return BigDecimal_to_i(ToValue(c));
- }
- return ToValue(c);
- }
- /* call-seq:
- * to_s(s)
- *
- * Converts the value to a string.
- *
- * The default format looks like 0.xxxxEnn.
- *
- * The optional parameter s consists of either an integer; or an optional '+'
- * or ' ', followed by an optional number, followed by an optional 'E' or 'F'.
- *
- * If there is a '+' at the start of s, positive values are returned with
- * a leading '+'.
- *
- * A space at the start of s returns positive values with a leading space.
- *
- * If s contains a number, a space is inserted after each group of that many
- * fractional digits.
- *
- * If s ends with an 'E', engineering notation (0.xxxxEnn) is used.
- *
- * If s ends with an 'F', conventional floating point notation is used.
- *
- * Examples:
- *
- * BigDecimal.new('-123.45678901234567890').to_s('5F') -> '-123.45678 90123 45678 9'
- *
- * BigDecimal.new('123.45678901234567890').to_s('+8F') -> '+123.45678901 23456789'
- *
- * BigDecimal.new('123.45678901234567890').to_s(' F') -> ' 123.4567890123456789'
- */
- static VALUE
- BigDecimal_to_s(int argc, VALUE *argv, VALUE self)
- {
- ENTER(5);
- int fmt=0; /* 0:E format */
- int fPlus=0; /* =0:default,=1: set ' ' before digits ,set '+' before digits. */
- Real *vp;
- volatile VALUE str;
- char *psz;
- char ch;
- size_t nc, mc = 0;
- VALUE f;
- GUARD_OBJ(vp,GetVpValue(self,1));
- if(rb_scan_args(argc,argv,"01",&f)==1) {
- if(TYPE(f)==T_STRING) {
- SafeStringValue(f);
- psz = RSTRING_PTR(f);
- if(*psz==' ') {
- fPlus = 1; psz++;
- } else if(*psz=='+') {
- fPlus = 2; psz++;
- }
- while((ch=*psz++)!=0) {
- if(ISSPACE(ch)) continue;
- if(!ISDIGIT(ch)) {
- if(ch=='F' || ch=='f') fmt = 1; /* F format */
- break;
- }
- mc = mc * 10 + ch - '0';
- }
- }
- else {
- mc = (size_t)GetPositiveInt(f);
- }
- }
- if(fmt) {
- nc = VpNumOfChars(vp,"F");
- } else {
- nc = VpNumOfChars(vp,"E");
- }
- if(mc>0) nc += (nc + mc - 1) / mc + 1;
- str = rb_str_new(0, nc);
- psz = RSTRING_PTR(str);
- if(fmt) {
- VpToFString(vp, psz, mc, fPlus);
- } else {
- VpToString (vp, psz, mc, fPlus);
- }
- rb_str_resize(str, strlen(psz));
- return str;
- }
- /* Splits a BigDecimal number into four parts, returned as an array of values.
- *
- * The first value represents the sign of the BigDecimal, and is -1 or 1, or 0
- * if the BigDecimal is Not a Number.
- *
- * The second value is a string representing the significant digits of the
- * BigDecimal, with no leading zeros.
- *
- * The third value is the base used for arithmetic (currently always 10) as an
- * Integer.
- *
- * The fourth value is an Integer exponent.
- *
- * If the BigDecimal can be represented as 0.xxxxxx*10**n, then xxxxxx is the
- * string of significant digits with no leading zeros, and n is the exponent.
- *
- * From these values, you can translate a BigDecimal to a float as follows:
- *
- * sign, significant_digits, base, exponent = a.split
- * f = sign * "0.#{significant_digits}".to_f * (base ** exponent)
- *
- * (Note that the to_f method is provided as a more convenient way to translate
- * a BigDecimal to a Float.)
- */
- static VALUE
- BigDecimal_split(VALUE self)
- {
- ENTER(5);
- Real *vp;
- VALUE obj,str;
- ssize_t e, s;
- char *psz1;
- GUARD_OBJ(vp,GetVpValue(self,1));
- str = rb_str_new(0, VpNumOfChars(vp,"E"));
- psz1 = RSTRING_PTR(str);
- VpSzMantissa(vp,psz1);
- s = 1;
- if(psz1[0]=='-') {
- size_t len = strlen(psz1+1);
- memmove(psz1, psz1+1, len);
- psz1[len] = '\0';
- s = -1;
- }
- if(psz1[0]=='N') s=0; /* NaN */
- e = VpExponent10(vp);
- obj = rb_ary_new2(4);
- rb_ary_push(obj, INT2FIX(s));
- rb_ary_push(obj, str);
- rb_str_resize(str, strlen(psz1));
- rb_ary_push(obj, INT2FIX(10));
- rb_ary_push(obj, INT2NUM(e));
- return obj;
- }
- /* Returns the exponent of the BigDecimal number, as an Integer.
- *
- * If the number can be represented as 0.xxxxxx*10**n where xxxxxx is a string
- * of digits with no leading zeros, then n is the exponent.
- */
- static VALUE
- BigDecimal_exponent(VALUE self)
- {
- ssize_t e = VpExponent10(GetVpValue(self, 1));
- return INT2NUM(e);
- }
- /* Returns debugging information about the value as a string of comma-separated
- * values in angle brackets with a leading #:
- *
- * BigDecimal.new("1234.5678").inspect ->
- * "#<BigDecimal:b7ea1130,'0.12345678E4',8(12)>"
- *
- * The first part is the address, the second is the value as a string, and
- * the final part ss(mm) is the current number of significant digits and the
- * maximum number of significant digits, respectively.
- */
- static VALUE
- BigDecimal_inspect(VALUE self)
- {
- ENTER(5);
- Real *vp;
- volatile VALUE obj;
- 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,"#<BigDecimal:%"PRIxVALUE",'",self);
- tmp = psz + strlen(psz);
- VpToString(vp, tmp, 10, 0);
- tmp += strlen(tmp);
- sprintf(tmp, "',%"PRIuSIZE"(%"PRIuSIZE")>", VpPrec(vp)*VpBaseFig(), VpMaxPrec(vp)*VpBaseFig());
- rb_str_resize(obj, strlen(psz));
- return obj;
- }
- /* call-seq:
- * power(n)
- *
- * Returns the value raised to the power of n. Note that n must be an Integer.
- *
- * Also available as the operator **
- */
- static VALUE
- BigDecimal_power(VALUE self, VALUE p)
- {
- ENTER(5);
- Real *x, *y;
- ssize_t mp, ma;
- SIGNED_VALUE n;
- Check_Type(p, T_FIXNUM);
- n = FIX2INT(p);
- ma = n;
- if (ma < 0) ma = -ma;
- if (ma == 0) ma = 1;
- GUARD_OBJ(x, GetVpValue(self, 1));
- if (VpIsDef(x)) {
- mp = x->Prec * (VpBaseFig() + 1);
- GUARD_OBJ(y, VpCreateRbObject(mp * (ma + 1), "0"));
- }
- else {
- GUARD_OBJ(y, VpCreateRbObject(1, "0"));
- }
- VpPower(y, x, n);
- return ToValue(y);
- }
- static VALUE
- BigDecimal_global_new(int argc, VALUE *argv, VALUE self)
- {
- ENTER(5);
- Real *pv;
- size_t mf;
- VALUE nFig;
- VALUE iniValue;
- if(rb_scan_args(argc,argv,"11",&iniValue,&nFig)==1) {
- mf = 0;
- } else {
- mf = GetPositiveInt(nFig);
- }
- SafeStringValue(iniValue);
- GUARD_OBJ(pv,VpCreateRbObject(mf, RSTRING_PTR(iniValue)));
- return ToValue(pv);
- }
- /* call-seq:
- * new(initial, digits)
- *
- * Create a new BigDecimal object.
- *
- * initial:: The initial value, as a String. Spaces are ignored, unrecognized characters terminate the value.
- *
- * digits:: The number of significant digits, as a Fixnum. If omitted or 0, the number of significant digits is determined from the initial value.
- *
- * The actual number of significant digits used in computation is usually
- * larger than the specified number.
- */
- static VALUE
- BigDecimal_new(int argc, VALUE *argv, VALUE self)
- {
- ENTER(5);
- Real *pv;
- size_t mf;
- VALUE nFig;
- VALUE iniValue;
- if(rb_scan_args(argc,argv,"11",&iniValue,&nFig)==1) {
- mf = 0;
- } else {
- mf = GetPositiveInt(nFig);
- }
- SafeStringValue(iniValue);
- GUARD_OBJ(pv,VpNewRbClass(mf, RSTRING_PTR(iniValue),self));
- return ToValue(pv);
- }
- /* call-seq:
- * BigDecimal.limit(digits)
- *
- * Limit the number of significant digits in newly created BigDecimal
- * numbers to the specified value. Rounding is performed as necessary,
- * as specified by BigDecimal.mode.
- *
- * A limit of 0, the default, means no upper limit.
- *
- * The limit specified by this method takes less priority over any limit
- * specified to instance methods such as ceil, floor, truncate, or round.
- */
- static VALUE
- BigDecimal_limit(int argc, VALUE *argv, VALUE self)
- {
- VALUE nFig;
- VALUE nCur = INT2NUM(VpGetPrecLimit());
- if(rb_scan_args(argc,argv,"01",&nFig)==1) {
- int nf;
- if(nFig==Qnil) return nCur;
- Check_Type(nFig, T_FIXNUM);
- nf = FIX2INT(nFig);
- if(nf<0) {
- rb_raise(rb_eArgError, "argument must be positive");
- }
- VpSetPrecLimit(nf);
- }
- return nCur;
- }
- /* Returns the sign of the value.
- *
- * Returns a positive value if > 0, a negative value if < 0, and a
- * zero if == 0.
- *
- * The specific value returned indicates the type and sign of the BigDecimal,
- * as follows:
- *
- * BigDecimal::SIGN_NaN:: value is Not a Number
- * BigDecimal::SIGN_POSITIVE_ZERO:: value is +0
- * BigDecimal::SIGN_NEGATIVE_ZERO:: value is -0
- * BigDecimal::SIGN_POSITIVE_INFINITE:: value is +infinity
- * BigDecimal::SIGN_NEGATIVE_INFINITE:: value is -infinity
- * BigDecimal::SIGN_POSITIVE_FINITE:: value is positive
- * BigDecimal::SIGN_NEGATIVE_FINITE:: value is negative
- */
- static VALUE
- BigDecimal_sign(VALUE self)
- { /* sign */
- int s = GetVpValue(self,1)->sign;
- return INT2FIX(s);
- }
- /* call-seq:
- * BigDecimal.save_exception_mode { ... }
- */
- static VALUE
- BigDecimal_save_exception_mode(VALUE self)
- {
- unsigned short const exception_mode = VpGetException();
- int state;
- VALUE ret = rb_protect(rb_yield, Qnil, &state);
- VpSetException(exception_mode);
- if (state) rb_jump_tag(state);
- return ret;
- }
- /* call-seq:
- * BigDecimal.save_rounding_mode { ... }
- */
- static VALUE
- BigDecimal_save_rounding_mode(VALUE self)
- {
- unsigned short const round_mode = VpGetRoundMode();
- int state;
- VALUE ret = rb_protect(rb_yield, Qnil, &state);
- VpSetRoundMode(round_mode);
- if (state) rb_jump_tag(state);
- return Qnil;
- }
- /* call-seq:
- * BigDecimal.save_limit { ... }
- */
- static VALUE
- BigDecimal_save_limit(VALUE self)
- {
- size_t const limit = VpGetPrecLimit();
- int state;
- VALUE ret = rb_protect(rb_yield, Qnil, &state);
- VpSetPrecLimit(limit);
- if (state) rb_jump_tag(state);
- return Qnil;
- }
- /* Document-class: BigDecimal
- * BigDecimal provides arbitrary-precision floating point decimal arithmetic.
- *
- * Copyright (C) 2002 by Shigeo Kobayashi <shigeo@tinyforest.gr.jp>.
- * You may distribute under the terms of either the GNU General Public
- * License or the Artistic License, as specified in the README file
- * of the BigDecimal distribution.
- *
- * Documented by mathew <meta@pobox.com>.
- *
- * = Introduction
- *
- * Ruby provides built-in support for arbitrary precision integer arithmetic.
- * For example:
- *
- * 42**13 -> 1265437718438866624512
- *
- * BigDecimal provides similar support for very large or very accurate floating
- * point numbers.
- *
- * Decimal arithmetic is also useful for general calculation, because it
- * provides the correct answers people expect--whereas normal binary floating
- * point arithmetic often introduces subtle errors because of the conversion
- * between base 10 and base 2. For example, try:
- *
- * sum = 0
- * for i in (1..10000)
- * sum = sum + 0.0001
- * end
- * print sum
- *
- * and contrast with the output from:
- *
- * require 'bigdecimal'
- *
- * sum = BigDecimal.new("0")
- * for i in (1..10000)
- * sum = sum + BigDecimal.new("0.0001")
- * end
- * print sum
- *
- * Similarly:
- *
- * (BigDecimal.new("1.2") - BigDecimal("1.0")) == BigDecimal("0.2") -> true
- *
- * (1.2 - 1.0) == 0.2 -> false
- *
- * = Special features of accurate decimal arithmetic
- *
- * Because BigDecimal is more accurate than normal binary floating point
- * arithmetic, it requires some special values.
- *
- * == Infinity
- *
- * BigDecimal sometimes needs to return infinity, for example if you divide
- * a value by zero.
- *
- * BigDecimal.new("1.0") / BigDecimal.new("0.0") -> infinity
- *
- * BigDecimal.new("-1.0") / BigDecimal.new("0.0") -> -infinity
- *
- * You can represent infinite numbers to BigDecimal using the strings
- * 'Infinity', '+Infinity' and '-Infinity' (case-sensitive)
- *
- * == Not a Number
- *
- * When a computation results in an undefined value, the special value NaN
- * (for 'not a number') is returned.
- *
- * Example:
- *
- * BigDecimal.new("0.0") / …
Large files files are truncated, but you can click here to view the full file