Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

4947 lines (4537 sloc) 130.884 kB
/*
*
* 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 "macruby_internal.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 = 0;
static ID id_BigDecimal_rounding_mode = 0;
static ID id_BigDecimal_precision_limit = 0;
#if 0
/* 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);}
#endif
// MacRuby does not need these macros since it has a real GC.
#define ENTER(n)
#define PUSH(x)
#define SAVE(p)
#define GUARD_OBJ(p,y) p=y
#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 ****
*/
#if 0
// XXX MACRUBY manually deleting the Real structure seems to cause a resurrection error
// because both the Free structure and the Ruby object have a cyclic reference.
static void
BigDecimal_delete(void *pv)
{
VpFree(pv);
}
#endif
#define BigDecimal_delete NULL
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_obj_is_kind_of(v, rb_cBigDecimal)) {
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));
const size_t len = VpNumOfChars(vp,"E")+50;
dump = rb_bstr_new_with_data(NULL, len);
psz = (char *)rb_bstr_bytes(dump);
snprintf(psz, len, "%ld:", VpMaxPrec(vp)*VpBaseFig());
VpToString(vp, psz+strlen(psz), 0, 0);
rb_bstr_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;
const unsigned char *pch;
unsigned char ch;
unsigned long m=0;
SafeStringValue(str);
pch = (const 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,(const char *)pch,self));
m /= VpBaseFig();
if(m && pv->MaxPrec>m) pv->MaxPrec = m+1;
return ToValue(pv);
}
/* 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:: round away from zero
* ROUND_DOWN:: round towards zero (truncate)
* ROUND_HALF_UP:: round up if the appropriate digit >= 5, otherwise truncate (default)
* ROUND_HALF_DOWN:: round up if the appropriate digit >= 6, otherwise truncate
* ROUND_HALF_EVEN:: round towards the even neighbor (Banker's rounding)
* ROUND_CEILING:: round towards positive infinity (ceil)
* ROUND_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_eTypeError, "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 */
fo = VpGetRoundMode();
if(val==Qnil) return INT2FIX(fo);
Check_Type(val, T_FIXNUM);
if(!VpIsRoundMode((unsigned short)FIX2INT(val))) {
rb_raise(rb_eTypeError, "invalid rounding mode");
return Qnil;
}
fo = VpSetRoundMode((unsigned short)FIX2INT(val));
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, const char *str, VALUE klass)
{
Real *pv = VpAlloc(mx,str);
GC_WB(&pv->obj, (VALUE)Data_Wrap_Struct(klass, 0, BigDecimal_delete, pv));
return pv;
}
VP_EXPORT Real *
VpCreateRbObject(size_t mx, const char *str)
{
Real *pv = VpAlloc(mx,str);
GC_WB(&pv->obj, (VALUE)Data_Wrap_Struct(rb_cBigDecimal, 0, BigDecimal_delete, 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;
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;
buf = xmalloc(VpNumOfChars(p,"E"));
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();
int na = rb_scan_args(argc,argv,"02",&vLoc,&vRound);
switch(na) {
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);
Check_Type(vRound, T_FIXNUM);
sw = (unsigned short)FIX2INT(vRound);
if(!VpIsRoundMode(sw)) {
rb_raise(rb_eTypeError, "invalid rounding mode");
return Qnil;
}
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;
const 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_bstr_new_with_data(0, nc);
char *buf = (char *)rb_bstr_bytes(str);
if(fmt) {
VpToFString(vp, buf, mc, fPlus);
} else {
VpToString (vp, buf, mc, fPlus);
}
rb_bstr_resize(str, strlen(buf));
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_bstr_new_with_data(0, VpNumOfChars(vp,"E"));
psz1 = (char *)rb_bstr_bytes(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_bstr_resize(str, strlen(psz1));
rb_ary_push(obj, str);
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_bstr_new_with_data(0, nc+256);
psz = (char *)rb_bstr_bytes(obj);
snprintf(psz,nc+256,"#<BigDecimal:%p,'",(void *)self);
tmp = psz + strlen(psz);
VpToString(vp, tmp, 10, 0);
tmp += strlen(tmp);
sprintf(tmp, "',%ld(%ld)>", VpPrec(vp)*VpBaseFig(), VpMaxPrec(vp)*VpBaseFig());
rb_bstr_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") / BigDecimal.new("0.0") -> NaN
*
* You can also create undefined values. NaN is never considered to be the
* same as any other value, even NaN itself:
*
* n = BigDecimal.new('NaN')
*
* n == 0.0 -> nil
*
* n == n -> nil
*
* == Positive and negative zero
*
* If a computation results in a value which is too small to be represented as
* a BigDecimal within the currently specified limits of precision, zero must
* be returned.
*
* If the value which is too small to be represented is negative, a BigDecimal
* value of negative zero is returned. If the value is positive, a value of
* positive zero is returned.
*
* BigDecimal.new("1.0") / BigDecimal.new("-Infinity") -> -0.0
*
* BigDecimal.new("1.0") / BigDecimal.new("Infinity") -> 0.0
*
* (See BigDecimal.mode for how to specify limits of precision.)
*
* Note that -0.0 and 0.0 are considered to be the same for the purposes of
* comparison.
*
* Note also that in mathematics, there is no particular concept of negative
* or positive zero; true mathematical zero has no sign.
*/
void
Init_bigdecimal(void)
{
VALUE arg;
id_BigDecimal_exception_mode = rb_intern("BigDecimal.exception_mode");
id_BigDecimal_rounding_mode = rb_intern("BigDecimal.rounding_mode");
id_BigDecimal_precision_limit = rb_intern("BigDecimal.precision_limit");
/* Initialize VP routines */
VpInit(0UL);
/* Class and method registration */
rb_cBigDecimal = rb_define_class("BigDecimal",rb_cNumeric);
/* Global function */
rb_define_global_function("BigDecimal", BigDecimal_global_new, -1);
/* Class methods */
rb_define_singleton_method(rb_cBigDecimal, "new", BigDecimal_new, -1);
rb_define_singleton_method(rb_cBigDecimal, "mode", BigDecimal_mode, -1);
rb_define_singleton_method(rb_cBigDecimal, "limit", BigDecimal_limit, -1);
rb_define_singleton_method(rb_cBigDecimal, "double_fig", BigDecimal_double_fig, 0);
rb_define_singleton_method(rb_cBigDecimal, "_load", BigDecimal_load, 1);
rb_define_singleton_method(rb_cBigDecimal, "ver", BigDecimal_version, 0);
rb_define_singleton_method(rb_cBigDecimal, "save_exception_mode", BigDecimal_save_exception_mode, 0);
rb_define_singleton_method(rb_cBigDecimal, "save_rounding_mode", BigDecimal_save_rounding_mode, 0);
rb_define_singleton_method(rb_cBigDecimal, "save_limit", BigDecimal_save_limit, 0);
/* Constants definition */
/*
* Base value used in internal calculations. On a 32 bit system, BASE
* is 10000, indicating that calculation is done in groups of 4 digits.
* (If it were larger, BASE**2 wouldn't fit in 32 bits, so you couldn't
* guarantee that two groups could always be multiplied together without
* overflow.)
*/
rb_define_const(rb_cBigDecimal, "BASE", INT2FIX((SIGNED_VALUE)VpBaseVal()));
/* Exceptions */
/*
* 0xff: Determines whether overflow, underflow or zero divide result in
* an exception being thrown. See BigDecimal.mode.
*/
rb_define_const(rb_cBigDecimal, "EXCEPTION_ALL",INT2FIX(VP_EXCEPTION_ALL));
/*
* 0x02: Determines what happens when the result of a computation is not a
* number (NaN). See BigDecimal.mode.
*/
rb_define_const(rb_cBigDecimal, "EXCEPTION_NaN",INT2FIX(VP_EXCEPTION_NaN));
/*
* 0x01: Determines what happens when the result of a computation is
* infinity. See BigDecimal.mode.
*/
rb_define_const(rb_cBigDecimal, "EXCEPTION_INFINITY",INT2FIX(VP_EXCEPTION_INFINITY));
/*
* 0x04: Determines what happens when the result of a computation is an
* underflow (a result too small to be represented). See BigDecimal.mode.
*/
rb_define_const(rb_cBigDecimal, "EXCEPTION_UNDERFLOW",INT2FIX(VP_EXCEPTION_UNDERFLOW));
/*
* 0x01: Determines what happens when the result of a computation is an
* overflow (a result too large to be represented). See BigDecimal.mode.
*/
rb_define_const(rb_cBigDecimal, "EXCEPTION_OVERFLOW",INT2FIX(VP_EXCEPTION_OVERFLOW));
/*
* 0x01: Determines what happens when a division by zero is performed.
* See BigDecimal.mode.
*/
rb_define_const(rb_cBigDecimal, "EXCEPTION_ZERODIVIDE",INT2FIX(VP_EXCEPTION_ZERODIVIDE));
/*
* 0x100: Determines what happens when a result must be rounded in order to
* fit in the appropriate number of significant digits. See
* BigDecimal.mode.
*/
rb_define_const(rb_cBigDecimal, "ROUND_MODE",INT2FIX(VP_ROUND_MODE));
/* 1: Indicates that values should be rounded away from zero. See
* BigDecimal.mode.
*/
rb_define_const(rb_cBigDecimal, "ROUND_UP",INT2FIX(VP_ROUND_UP));
/* 2: Indicates that values should be rounded towards zero. See
* BigDecimal.mode.
*/
rb_define_const(rb_cBigDecimal, "ROUND_DOWN",INT2FIX(VP_ROUND_DOWN));
/* 3: Indicates that digits >= 5 should be rounded up, others rounded down.
* See BigDecimal.mode. */
rb_define_const(rb_cBigDecimal, "ROUND_HALF_UP",INT2FIX(VP_ROUND_HALF_UP));
/* 4: Indicates that digits >= 6 should be rounded up, others rounded down.
* See BigDecimal.mode.
*/
rb_define_const(rb_cBigDecimal, "ROUND_HALF_DOWN",INT2FIX(VP_ROUND_HALF_DOWN));
/* 5: Round towards +infinity. See BigDecimal.mode. */
rb_define_const(rb_cBigDecimal, "ROUND_CEILING",INT2FIX(VP_ROUND_CEIL));
/* 6: Round towards -infinity. See BigDecimal.mode. */
rb_define_const(rb_cBigDecimal, "ROUND_FLOOR",INT2FIX(VP_ROUND_FLOOR));
/* 7: Round towards the even neighbor. See BigDecimal.mode. */
rb_define_const(rb_cBigDecimal, "ROUND_HALF_EVEN",INT2FIX(VP_ROUND_HALF_EVEN));
/* 0: Indicates that a value is not a number. See BigDecimal.sign. */
rb_define_const(rb_cBigDecimal, "SIGN_NaN",INT2FIX(VP_SIGN_NaN));
/* 1: Indicates that a value is +0. See BigDecimal.sign. */
rb_define_const(rb_cBigDecimal, "SIGN_POSITIVE_ZERO",INT2FIX(VP_SIGN_POSITIVE_ZERO));
/* -1: Indicates that a value is -0. See BigDecimal.sign. */
rb_define_const(rb_cBigDecimal, "SIGN_NEGATIVE_ZERO",INT2FIX(VP_SIGN_NEGATIVE_ZERO));
/* 2: Indicates that a value is positive and finite. See BigDecimal.sign. */
rb_define_const(rb_cBigDecimal, "SIGN_POSITIVE_FINITE",INT2FIX(VP_SIGN_POSITIVE_FINITE));
/* -2: Indicates that a value is negative and finite. See BigDecimal.sign. */
rb_define_const(rb_cBigDecimal, "SIGN_NEGATIVE_FINITE",INT2FIX(VP_SIGN_NEGATIVE_FINITE));
/* 3: Indicates that a value is positive and infinite. See BigDecimal.sign. */
rb_define_const(rb_cBigDecimal, "SIGN_POSITIVE_INFINITE",INT2FIX(VP_SIGN_POSITIVE_INFINITE));
/* -3: Indicates that a value is negative and infinite. See BigDecimal.sign. */
rb_define_const(rb_cBigDecimal, "SIGN_NEGATIVE_INFINITE",INT2FIX(VP_SIGN_NEGATIVE_INFINITE));
arg = rb_str_new2("+Infinity");
rb_define_const(rb_cBigDecimal, "INFINITY", BigDecimal_global_new(1, &arg, rb_cBigDecimal));
arg = rb_str_new2("NaN");
rb_define_const(rb_cBigDecimal, "NAN", BigDecimal_global_new(1, &arg, rb_cBigDecimal));
/* instance methods */
rb_define_method(rb_cBigDecimal, "precs", BigDecimal_prec, 0);
rb_define_method(rb_cBigDecimal, "add", BigDecimal_add2, 2);
rb_define_method(rb_cBigDecimal, "sub", BigDecimal_sub2, 2);
rb_define_method(rb_cBigDecimal, "mult", BigDecimal_mult2, 2);
rb_define_method(rb_cBigDecimal, "div", BigDecimal_div2, -1);
rb_define_method(rb_cBigDecimal, "hash", BigDecimal_hash, 0);
rb_define_method(rb_cBigDecimal, "to_s", BigDecimal_to_s, -1);
rb_define_method(rb_cBigDecimal, "to_i", BigDecimal_to_i, 0);
rb_define_method(rb_cBigDecimal, "to_int", BigDecimal_to_i, 0);
rb_define_method(rb_cBigDecimal, "to_r", BigDecimal_to_r, 0);
rb_define_method(rb_cBigDecimal, "split", BigDecimal_split, 0);
rb_define_method(rb_cBigDecimal, "+", BigDecimal_add, 1);
rb_define_method(rb_cBigDecimal, "-", BigDecimal_sub, 1);
rb_define_method(rb_cBigDecimal, "+@", BigDecimal_uplus, 0);
rb_define_method(rb_cBigDecimal, "-@", BigDecimal_neg, 0);
rb_define_method(rb_cBigDecimal, "*", BigDecimal_mult, 1);
rb_define_method(rb_cBigDecimal, "/", BigDecimal_div, 1);
rb_define_method(rb_cBigDecimal, "quo", BigDecimal_div, 1);
rb_define_method(rb_cBigDecimal, "%", BigDecimal_mod, 1);
rb_define_method(rb_cBigDecimal, "modulo", BigDecimal_mod, 1);
rb_define_method(rb_cBigDecimal, "remainder", BigDecimal_remainder, 1);
rb_define_method(rb_cBigDecimal, "divmod", BigDecimal_divmod, 1);
/* rb_define_method(rb_cBigDecimal, "dup", BigDecimal_dup, 0); */
rb_define_method(rb_cBigDecimal, "to_f", BigDecimal_to_f, 0);
rb_define_method(rb_cBigDecimal, "abs", BigDecimal_abs, 0);
rb_define_method(rb_cBigDecimal, "sqrt", BigDecimal_sqrt, 1);
rb_define_method(rb_cBigDecimal, "fix", BigDecimal_fix, 0);
rb_define_method(rb_cBigDecimal, "round", BigDecimal_round, -1);
rb_define_method(rb_cBigDecimal, "frac", BigDecimal_frac, 0);
rb_define_method(rb_cBigDecimal, "floor", BigDecimal_floor, -1);
rb_define_method(rb_cBigDecimal, "ceil", BigDecimal_ceil, -1);
rb_define_method(rb_cBigDecimal, "power", BigDecimal_power, 1);
rb_define_method(rb_cBigDecimal, "**", BigDecimal_power, 1);
rb_define_method(rb_cBigDecimal, "<=>", BigDecimal_comp, 1);
rb_define_method(rb_cBigDecimal, "==", BigDecimal_eq, 1);
rb_define_method(rb_cBigDecimal, "===", BigDecimal_eq, 1);
rb_define_method(rb_cBigDecimal, "eql?", BigDecimal_eq, 1);
rb_define_method(rb_cBigDecimal, "<", BigDecimal_lt, 1);
rb_define_method(rb_cBigDecimal, "<=", BigDecimal_le, 1);
rb_define_method(rb_cBigDecimal, ">", BigDecimal_gt, 1);
rb_define_method(rb_cBigDecimal, ">=", BigDecimal_ge, 1);
rb_define_method(rb_cBigDecimal, "zero?", BigDecimal_zero, 0);
rb_define_method(rb_cBigDecimal, "nonzero?", BigDecimal_nonzero, 0);
rb_define_method(rb_cBigDecimal, "coerce", BigDecimal_coerce, 1);
rb_define_method(rb_cBigDecimal, "inspect", BigDecimal_inspect, 0);
rb_define_method(rb_cBigDecimal, "exponent", BigDecimal_exponent, 0);
rb_define_method(rb_cBigDecimal, "sign", BigDecimal_sign, 0);
rb_define_method(rb_cBigDecimal, "nan?", BigDecimal_IsNaN, 0);
rb_define_method(rb_cBigDecimal, "infinite?", BigDecimal_IsInfinite, 0);
rb_define_method(rb_cBigDecimal, "finite?", BigDecimal_IsFinite, 0);
rb_define_method(rb_cBigDecimal, "truncate", BigDecimal_truncate, -1);
rb_define_method(rb_cBigDecimal, "_dump", BigDecimal_dump, -1);
}
/*
*
* ============================================================================
*
* vp_ routines begin from here.
*
* ============================================================================
*
*/
#ifdef BIGDECIMAL_DEBUG
static int gfDebug = 1; /* Debug switch */
#if 0
static int gfCheckVal = 1; /* Value checking flag in VpNmlz() */
#endif
#endif /* BIGDECIMAL_DEBUG */
static Real *VpConstOne; /* constant 1.0 */
static Real *VpPt5; /* constant 0.5 */
#define maxnr 100UL /* Maximum iterations for calcurating sqrt. */
/* used in VpSqrt() */
/* ETC */
#define MemCmp(x,y,z) memcmp(x,y,z)
#define StrCmp(x,y) strcmp(x,y)
static int VpIsDefOP(Real *c,Real *a,Real *b,int sw);
static int AddExponent(Real *a, SIGNED_VALUE n);
static BDIGIT VpAddAbs(Real *a,Real *b,Real *c);
static BDIGIT VpSubAbs(Real *a,Real *b,Real *c);
static size_t VpSetPTR(Real *a, Real *b, Real *c, size_t *a_pos, size_t *b_pos, size_t *c_pos, BDIGIT *av, BDIGIT *bv);
static int VpNmlz(Real *a);
static void VpFormatSt(char *psz, size_t fFmt);
static int VpRdup(Real *m, size_t ind_m);
#ifdef BIGDECIMAL_DEBUG
static int gnAlloc=0; /* Memory allocation counter */
#endif /* BIGDECIMAL_DEBUG */
VP_EXPORT void *
VpMemAlloc(size_t mb)
{
void *p = xmalloc(mb);
if(!p) {
VpException(VP_EXCEPTION_MEMORY,"failed to allocate memory",1);
}
memset(p,0,mb);
#ifdef BIGDECIMAL_DEBUG
gnAlloc++; /* Count allocation call */
#endif /* BIGDECIMAL_DEBUG */
return p;
}
VP_EXPORT void
VpFree(Real *pv)
{
if(pv != NULL) {
xfree(pv);
#ifdef BIGDECIMAL_DEBUG
gnAlloc--; /* Decrement allocation count */
if(gnAlloc==0) {
printf(" *************** All memories allocated freed ****************");
getchar();
}
if(gnAlloc<0) {
printf(" ??????????? Too many memory free calls(%d) ?????????????\n",gnAlloc);
getchar();
}
#endif /* BIGDECIMAL_DEBUG */
}
}
/*
* EXCEPTION Handling.
*/
#define rmpd_set_thread_local_exception_mode(mode) \
rb_thread_local_aset( \
rb_thread_current(), \
id_BigDecimal_exception_mode, \
INT2FIX((int)(mode)) \
)
static unsigned short
VpGetException (void)
{
VALUE const vmode = rb_thread_local_aref(
rb_thread_current(),
id_BigDecimal_exception_mode
);
if (NIL_P(vmode)) {
rmpd_set_thread_local_exception_mode(RMPD_EXCEPTION_MODE_DEFAULT);
return RMPD_EXCEPTION_MODE_DEFAULT;
}
return (unsigned short)FIX2UINT(vmode);
}
static void
VpSetException(unsigned short f)
{
rmpd_set_thread_local_exception_mode(f);
}
/*
* Precision limit.
*/
#define rmpd_set_thread_local_precision_limit(limit) \
rb_thread_local_aset( \
rb_thread_current(), \
id_BigDecimal_precision_limit, \
SIZET2NUM(limit) \
)
#define RMPD_PRECISION_LIMIT_DEFAULT ((size_t)0)
/* These 2 functions added at v1.1.7 */
VP_EXPORT size_t
VpGetPrecLimit(void)
{
VALUE const vlimit = rb_thread_local_aref(
rb_thread_current(),
id_BigDecimal_precision_limit
);
if (NIL_P(vlimit)) {
rmpd_set_thread_local_precision_limit(RMPD_PRECISION_LIMIT_DEFAULT);
return RMPD_PRECISION_LIMIT_DEFAULT;
}
return NUM2SIZET(vlimit);
}
VP_EXPORT size_t
VpSetPrecLimit(size_t n)
{
size_t const s = VpGetPrecLimit();
rmpd_set_thread_local_precision_limit(n);
return s;
}
/*
* Rounding mode.
*/
#define rmpd_set_thread_local_rounding_mode(mode) \
rb_thread_local_aset( \
rb_thread_current(), \
id_BigDecimal_rounding_mode, \
INT2FIX((int)(mode)) \
)
VP_EXPORT unsigned short
VpGetRoundMode(void)
{
VALUE const vmode = rb_thread_local_aref(
rb_thread_current(),
id_BigDecimal_rounding_mode
);
if (NIL_P(vmode)) {
rmpd_set_thread_local_rounding_mode(RMPD_ROUNDING_MODE_DEFAULT);
return RMPD_ROUNDING_MODE_DEFAULT;
}
return (unsigned short)FIX2INT(vmode);
}
VP_EXPORT int
VpIsRoundMode(unsigned short n)
{
switch (n) {
case VP_ROUND_UP:
case VP_ROUND_DOWN:
case VP_ROUND_HALF_UP:
case VP_ROUND_HALF_DOWN:
case VP_ROUND_CEIL:
case VP_ROUND_FLOOR:
case VP_ROUND_HALF_EVEN:
return 1;
default:
return 0;
}
}
VP_EXPORT unsigned short
VpSetRoundMode(unsigned short n)
{
if (VpIsRoundMode(n)) {
rmpd_set_thread_local_rounding_mode(n);
return n;
}
return VpGetRoundMode();
}
/*
* 0.0 & 1.0 generator
* These gZero_..... and gOne_..... can be any name
* referenced from nowhere except Zero() and One().
* gZero_..... and gOne_..... must have global scope
* (to let the compiler know they may be changed in outside
* (... but not actually..)).
*/
volatile const double gZero_ABCED9B1_CE73__00400511F31D = 0.0;
volatile const double gOne_ABCED9B4_CE73__00400511F31D = 1.0;
static double
Zero(void)
{
return gZero_ABCED9B1_CE73__00400511F31D;
}
static double
One(void)
{
return gOne_ABCED9B4_CE73__00400511F31D;
}
/*
----------------------------------------------------------------
Value of sign in Real structure is reserved for future use.
short sign;
==0 : NaN
1 : Positive zero
-1 : Negative zero
2 : Positive number
-2 : Negative number
3 : Positive infinite number
-3 : Negative infinite number
----------------------------------------------------------------
*/
VP_EXPORT double
VpGetDoubleNaN(void) /* Returns the value of NaN */
{
static double fNaN = 0.0;
if(fNaN==0.0) fNaN = Zero()/Zero();
return fNaN;
}
VP_EXPORT double
VpGetDoublePosInf(void) /* Returns the value of +Infinity */
{
static double fInf = 0.0;
if(fInf==0.0) fInf = One()/Zero();
return fInf;
}
VP_EXPORT double
VpGetDoubleNegInf(void) /* Returns the value of -Infinity */
{
static double fInf = 0.0;
if(fInf==0.0) fInf = -(One()/Zero());
return fInf;
}
VP_EXPORT double
VpGetDoubleNegZero(void) /* Returns the value of -0 */
{
static double nzero = 1000.0;
if(nzero!=0.0) nzero = (One()/VpGetDoubleNegInf());
return nzero;
}
#if 0 /* unused */
VP_EXPORT int
VpIsNegDoubleZero(double v)
{
double z = VpGetDoubleNegZero();
return MemCmp(&v,&z,sizeof(v))==0;
}
#endif
VP_EXPORT int
VpException(unsigned short f, const char *str,int always)
{
VALUE exc;
int fatal=0;
unsigned short const exception_mode = VpGetException();
if(f==VP_EXCEPTION_OP || f==VP_EXCEPTION_MEMORY) always = 1;
if (always || (exception_mode & f)) {
switch(f)
{
/*
case VP_EXCEPTION_OVERFLOW:
*/
case VP_EXCEPTION_ZERODIVIDE:
case VP_EXCEPTION_INFINITY:
case VP_EXCEPTION_NaN:
case VP_EXCEPTION_UNDERFLOW:
case VP_EXCEPTION_OP:
exc = rb_eFloatDomainError;
goto raise;
case VP_EXCEPTION_MEMORY:
fatal = 1;
goto raise;
default:
fatal = 1;
goto raise;
}
}
return 0; /* 0 Means VpException() raised no exception */
raise:
if(fatal) rb_fatal("%s", str);
else rb_raise(exc, "%s", str);
return 0;
}
/* Throw exception or returns 0,when resulting c is Inf or NaN */
/* sw=1:+ 2:- 3:* 4:/ */
static int
VpIsDefOP(Real *c,Real *a,Real *b,int sw)
{
if(VpIsNaN(a) || VpIsNaN(b)) {
/* at least a or b is NaN */
VpSetNaN(c);
goto NaN;
}
if(VpIsInf(a)) {
if(VpIsInf(b)) {
switch(sw)
{
case 1: /* + */
if(VpGetSign(a)==VpGetSign(b)) {
VpSetInf(c,VpGetSign(a));
goto Inf;
} else {
VpSetNaN(c);
goto NaN;
}
case 2: /* - */
if(VpGetSign(a)!=VpGetSign(b)) {
VpSetInf(c,VpGetSign(a));
goto Inf;
} else {
VpSetNaN(c);
goto NaN;
}
break;
case 3: /* * */
VpSetInf(c,VpGetSign(a)*VpGetSign(b));
goto Inf;
break;
case 4: /* / */
VpSetNaN(c);
goto NaN;
}
VpSetNaN(c);
goto NaN;
}
/* Inf op Finite */
switch(sw)
{
case 1: /* + */
case 2: /* - */
VpSetInf(c,VpGetSign(a));
break;
case 3: /* * */
if(VpIsZero(b)) {
VpSetNaN(c);
goto NaN;
}
VpSetInf(c,VpGetSign(a)*VpGetSign(b));
break;
case 4: /* / */
VpSetInf(c,VpGetSign(a)*VpGetSign(b));
}
goto Inf;
}
if(VpIsInf(b)) {
switch(sw)
{
case 1: /* + */
VpSetInf(c,VpGetSign(b));
break;
case 2: /* - */
VpSetInf(c,-VpGetSign(b));
break;
case 3: /* * */
if(VpIsZero(a)) {
VpSetNaN(c);
goto NaN;
}
VpSetInf(c,VpGetSign(a)*VpGetSign(b));
break;
case 4: /* / */
VpSetZero(c,VpGetSign(a)*VpGetSign(b));
}
goto Inf;
}
return 1; /* Results OK */
Inf:
return VpException(VP_EXCEPTION_INFINITY,"Computation results to 'Infinity'",0);
NaN:
return VpException(VP_EXCEPTION_NaN,"Computation results to 'NaN'",0);
}
/*
----------------------------------------------------------------
*/
/*
* returns number of chars needed to represent vp in specified format.
*/
VP_EXPORT size_t
VpNumOfChars(Real *vp,const char *pszFmt)
{
SIGNED_VALUE ex;
size_t nc;
if(vp == NULL) return BASE_FIG*2+6;
if(!VpIsDef(vp)) return 32; /* not sure,may be OK */
switch(*pszFmt)
{
case 'F':
nc = BASE_FIG*(vp->Prec + 1)+2;
ex = vp->exponent;
if(ex < 0) {
nc += BASE_FIG*(size_t)(-ex);
}
else {
if((size_t)ex > vp->Prec) {
nc += BASE_FIG*((size_t)ex - vp->Prec);
}
}
break;
case 'E':
default:
nc = BASE_FIG*(vp->Prec + 2)+6; /* 3: sign + exponent chars */
}
return nc;
}
/*
* Initializer for Vp routines and constants used.
* [Input]
* BaseVal: Base value(assigned to BASE) for Vp calculation.
* It must be the form BaseVal=10**n.(n=1,2,3,...)
* If Base <= 0L,then the BASE will be calcurated so
* that BASE is as large as possible satisfying the
* relation MaxVal <= BASE*(BASE+1). Where the value
* MaxVal is the largest value which can be represented
* by one BDIGIT word in the computer used.
*
* [Returns]
* 1+DBL_DIG ... OK
*/
VP_EXPORT size_t
VpInit(BDIGIT BaseVal)
{
/* Setup +/- Inf NaN -0 */
VpGetDoubleNaN();
VpGetDoublePosInf();
VpGetDoubleNegInf();
VpGetDoubleNegZero();
/* Allocates Vp constants. */
VpConstOne = VpAlloc(1UL, "1");
VpPt5 = VpAlloc(1UL, ".5");
GC_RETAIN(VpConstOne);
GC_RETAIN(VpPt5);
#ifdef BIGDECIMAL_DEBUG
gnAlloc = 0;
#endif /* BIGDECIMAL_DEBUG */
#ifdef BIGDECIMAL_DEBUG
if(gfDebug) {
printf("VpInit: BaseVal = %lu\n", BaseVal);
printf(" BASE = %lu\n", BASE);
printf(" HALF_BASE = %lu\n", HALF_BASE);
printf(" BASE1 = %lu\n", BASE1);
printf(" BASE_FIG = %u\n", BASE_FIG);
printf(" DBLE_FIG = %d\n", DBLE_FIG);
}
#endif /* BIGDECIMAL_DEBUG */
return rmpd_double_figures();
}
VP_EXPORT Real *
VpOne(void)
{
return VpConstOne;
}
/* If exponent overflows,then raise exception or returns 0 */
static int
AddExponent(Real *a, SIGNED_VALUE n)
{
SIGNED_VALUE e = a->exponent;
SIGNED_VALUE m = e+n;
SIGNED_VALUE eb, mb;
if(e>0) {
if(n>0) {
mb = m*(SIGNED_VALUE)BASE_FIG;
eb = e*(SIGNED_VALUE)BASE_FIG;
if(mb<eb) goto overflow;
}
} else if(n<0) {
mb = m*(SIGNED_VALUE)BASE_FIG;
eb = e*(SIGNED_VALUE)BASE_FIG;
if(mb>eb) goto underflow;
}
a->exponent = m;
return 1;
/* Overflow/Underflow ==> Raise exception or returns 0 */
underflow:
VpSetZero(a,VpGetSign(a));
return VpException(VP_EXCEPTION_UNDERFLOW,"Exponent underflow",0);
overflow:
VpSetInf(a,VpGetSign(a));
return VpException(VP_EXCEPTION_OVERFLOW,"Exponent overflow",0);
}
/*
* Allocates variable.
* [Input]
* mx ... allocation unit, if zero then mx is determined by szVal.
* The mx is the number of effective digits can to be stored.
* szVal ... value assigned(char). If szVal==NULL,then zero is assumed.
* If szVal[0]=='#' then Max. Prec. will not be considered(1.1.7),
* full precision specified by szVal is allocated.
*
* [Returns]
* Pointer to the newly allocated variable, or
* NULL be returned if memory allocation is failed,or any error.
*/
VP_EXPORT Real *
VpAlloc(size_t mx, const char *szVal)
{
size_t i, ni, ipn, ipf, nf, ipe, ne, nalloc;
char v,*psz;
int sign=1;
Real *vp = NULL;
size_t mf = VpGetPrecLimit();
mx = (mx + BASE_FIG - 1) / BASE_FIG + 1; /* Determine allocation unit. */
if (szVal) {
while (ISSPACE(*szVal)) szVal++;
if (*szVal != '#') {
if (mf) {
mf = (mf + BASE_FIG - 1) / BASE_FIG + 2; /* Needs 1 more for div */
if (mx > mf) {
mx = mf;
}
}
}
else {
++szVal;
}
}
else {
/* necessary to be able to store */
/* at least mx digits. */
/* szVal==NULL ==> allocate zero value. */
vp = (Real *) VpMemAlloc(sizeof(Real) + mx * sizeof(BDIGIT));
/* xmalloc() alway returns(or throw interruption) */
vp->MaxPrec = mx; /* set max precision */
VpSetZero(vp,1); /* initialize vp to zero. */
return vp;
}
/* Skip all '_' after digit: 2006-6-30 */
ni = 0;
psz = xmalloc(strlen(szVal) + 1);
i = 0;
ipn = 0;
while ((psz[i]=szVal[ipn]) != 0) {
if (ISDIGIT(psz[i])) ++ni;
if (psz[i] == '_') {
if (ni > 0) { ipn++; continue; }
psz[i] = 0;
break;
}
++i;
++ipn;
}
/* Skip trailing spaces */
while (--i > 0) {
if (ISSPACE(psz[i])) psz[i] = 0;
else break;
}
szVal = psz;
/* Check on Inf & NaN */
if (StrCmp(szVal, SZ_PINF) == 0 ||
StrCmp(szVal, SZ_INF) == 0 ) {
vp = (Real *) VpMemAlloc(sizeof(Real) + sizeof(BDIGIT));
vp->MaxPrec = 1; /* set max precision */
VpSetPosInf(vp);
return vp;
}
if (StrCmp(szVal, SZ_NINF) == 0) {
vp = (Real *) VpMemAlloc(sizeof(Real) + sizeof(BDIGIT));
vp->MaxPrec = 1; /* set max precision */
VpSetNegInf(vp);
return vp;
}
if (StrCmp(szVal, SZ_NaN) == 0) {
vp = (Real *) VpMemAlloc(sizeof(Real) + sizeof(BDIGIT));
vp->MaxPrec = 1; /* set max precision */
VpSetNaN(vp);
return vp;
}
/* check on number szVal[] */
ipn = i = 0;
if (szVal[i] == '-') { sign=-1; ++i; }
else if (szVal[i] == '+') ++i;
/* Skip digits */
ni = 0; /* digits in mantissa */
while ((v = szVal[i]) != 0) {
if (!ISDIGIT(v)) break;
++i;
++ni;
}
nf = 0;
ipf = 0;
ipe = 0;
ne = 0;
if (v) {
/* other than digit nor \0 */
if (szVal[i] == '.') { /* xxx. */
++i;
ipf = i;
while ((v = szVal[i]) != 0) { /* get fraction part. */
if (!ISDIGIT(v)) break;
++i;
++nf;
}
}
ipe = 0; /* Exponent */
switch (szVal[i]) {
case '\0':
break;
case 'e': case 'E':
case 'd': case 'D':
++i;
ipe = i;
v = szVal[i];
if ((v == '-') || (v == '+')) ++i;
while ((v=szVal[i]) != 0) {
if (!ISDIGIT(v)) break;
++i;
++ne;
}
break;
default:
break;
}
}
nalloc = (ni + nf + BASE_FIG - 1) / BASE_FIG + 1; /* set effective allocation */
/* units for szVal[] */
if (mx <= 0) mx = 1;
nalloc = Max(nalloc, mx);
mx = nalloc;
vp = (Real *) VpMemAlloc(sizeof(Real) + mx * sizeof(BDIGIT));
/* xmalloc() alway returns(or throw interruption) */
vp->MaxPrec = mx; /* set max precision */
VpSetZero(vp, sign);
VpCtoV(vp, &szVal[ipn], ni, &szVal[ipf], nf, &szVal[ipe], ne);
return vp;
}
/*
* Assignment(c=a).
* [Input]
* a ... RHSV
* isw ... switch for assignment.
* c = a when isw > 0
* c = -a when isw < 0
* if c->MaxPrec < a->Prec,then round operation
* will be performed.
* [Output]
* c ... LHSV
*/
VP_EXPORT size_t
VpAsgn(Real *c, Real *a, int isw)
{
size_t n;
if(VpIsNaN(a)) {
VpSetNaN(c);
return 0;
}
if(VpIsInf(a)) {
VpSetInf(c,isw*VpGetSign(a));
return 0;
}
/* check if the RHS is zero */
if(!VpIsZero(a)) {
c->exponent = a->exponent; /* store exponent */
VpSetSign(c,(isw*VpGetSign(a))); /* set sign */
n =(a->Prec < c->MaxPrec) ?(a->Prec) :(c->MaxPrec);
c->Prec = n;
memcpy(c->frac, a->frac, n * sizeof(BDIGIT));
/* Needs round ? */
if(isw!=10) {
/* Not in ActiveRound */
if(c->Prec < a->Prec) {
VpInternalRound(c,n,(n>0)?a->frac[n-1]:0,a->frac[n]);
} else {
VpLimitRound(c,0);
}
}
} else {
/* The value of 'a' is zero. */
VpSetZero(c,isw*VpGetSign(a));
return 1;
}
return c->Prec*BASE_FIG;
}
/*
* c = a + b when operation = 1 or 2
* = a - b when operation = -1 or -2.
* Returns number of significant digits of c
*/
VP_EXPORT size_t
VpAddSub(Real *c, Real *a, Real *b, int operation)
{
short sw, isw;
Real *a_ptr, *b_ptr;
size_t n, na, nb, i;
BDIGIT mrv;
#ifdef BIGDECIMAL_DEBUG
if(gfDebug) {
VPrint(stdout, "VpAddSub(enter) a=% \n", a);
VPrint(stdout, " b=% \n", b);
printf(" operation=%d\n", operation);
}
#endif /* BIGDECIMAL_DEBUG */
if(!VpIsDefOP(c,a,b,(operation>0)?1:2)) return 0; /* No significant digits */
/* check if a or b is zero */
if(VpIsZero(a)) {
/* a is zero,then assign b to c */
if(!VpIsZero(b)) {
VpAsgn(c, b, operation);
} else {
/* Both a and b are zero. */
if(VpGetSign(a)<0 && operation*VpGetSign(b)<0) {
/* -0 -0 */
VpSetZero(c,-1);
} else {
VpSetZero(c,1);
}
return 1; /* 0: 1 significant digits */
}
return c->Prec*BASE_FIG;
}
if(VpIsZero(b)) {
/* b is zero,then assign a to c. */
VpAsgn(c, a, 1);
return c->Prec*BASE_FIG;
}
if(operation < 0) sw = -1;
else sw = 1;
/* compare absolute value. As a result,|a_ptr|>=|b_ptr| */
if(a->exponent > b->exponent) {
a_ptr = a;
b_ptr = b;
} /* |a|>|b| */
else if(a->exponent < b->exponent) {
a_ptr = b;
b_ptr = a;
} /* |a|<|b| */
else {
/* Exponent part of a and b is the same,then compare fraction */
/* part */
na = a->Prec;
nb = b->Prec;
n = Min(na, nb);
for(i=0;i < n; ++i) {
if(a->frac[i] > b->frac[i]) {
a_ptr = a;
b_ptr = b;
goto end_if;
} else if(a->frac[i] < b->frac[i]) {
a_ptr = b;
b_ptr = a;
goto end_if;
}
}
if(na > nb) {
a_ptr = a;
b_ptr = b;
goto end_if;
} else if(na < nb) {
a_ptr = b;
b_ptr = a;
goto end_if;
}
/* |a| == |b| */
if(VpGetSign(a) + sw *VpGetSign(b) == 0) {
VpSetZero(c,1); /* abs(a)=abs(b) and operation = '-' */
return c->Prec*BASE_FIG;
}
a_ptr = a;
b_ptr = b;
}
end_if:
isw = VpGetSign(a) + sw *VpGetSign(b);
/*
* isw = 0 ...( 1)+(-1),( 1)-( 1),(-1)+(1),(-1)-(-1)
* = 2 ...( 1)+( 1),( 1)-(-1)
* =-2 ...(-1)+(-1),(-1)-( 1)
* If isw==0, then c =(Sign a_ptr)(|a_ptr|-|b_ptr|)
* else c =(Sign ofisw)(|a_ptr|+|b_ptr|)
*/
if(isw) { /* addition */
VpSetSign(c, 1);
mrv = VpAddAbs(a_ptr, b_ptr, c);
VpSetSign(c, isw / 2);
} else { /* subtraction */
VpSetSign(c, 1);
mrv = VpSubAbs(a_ptr, b_ptr, c);
if(a_ptr == a) {
VpSetSign(c,VpGetSign(a));
} else {
VpSetSign(c,VpGetSign(a_ptr) * sw);
}
}
VpInternalRound(c,0,(c->Prec>0)?c->frac[c->Prec-1]:0,mrv);
#ifdef BIGDECIMAL_DEBUG
if(gfDebug) {
VPrint(stdout, "VpAddSub(result) c=% \n", c);
VPrint(stdout, " a=% \n", a);
VPrint(stdout, " b=% \n", b);
printf(" operation=%d\n", operation);
}
#endif /* BIGDECIMAL_DEBUG */
return c->Prec*BASE_FIG;
}
/*
* Addition of two variable precisional variables
* a and b assuming abs(a)>abs(b).
* c = abs(a) + abs(b) ; where |a|>=|b|
*/
static BDIGIT
VpAddAbs(Real *a, Real *b, Real *c)
{
size_t word_shift;
size_t ap;
size_t bp;
size_t cp;
size_t a_pos;
size_t b_pos, b_pos_with_word_shift;
size_t c_pos;
BDIGIT av, bv, carry, mrv;
#ifdef BIGDECIMAL_DEBUG
if(gfDebug) {
VPrint(stdout, "VpAddAbs called: a = %\n", a);
VPrint(stdout, " b = %\n", b);
}
#endif /* BIGDECIMAL_DEBUG */
word_shift = VpSetPTR(a, b, c, &ap, &bp, &cp, &av, &bv);
a_pos = ap;
b_pos = bp;
c_pos = cp;
if(word_shift==(size_t)-1L) return 0; /* Overflow */
if(b_pos == (size_t)-1L) goto Assign_a;
mrv = av + bv; /* Most right val. Used for round. */
/* Just assign the last few digits of b to c because a has no */
/* corresponding digits to be added. */
while(b_pos + word_shift > a_pos) {
--c_pos;
if(b_pos > 0) {
c->frac[c_pos] = b->frac[--b_pos];
} else {
--word_shift;
c->frac[c_pos] = 0;
}
}
/* Just assign the last few digits of a to c because b has no */
/* corresponding digits to be added. */
b_pos_with_word_shift = b_pos + word_shift;
while(a_pos > b_pos_with_word_shift) {
c->frac[--c_pos] = a->frac[--a_pos];
}
carry = 0; /* set first carry be zero */
/* Now perform addition until every digits of b will be */
/* exhausted. */
while(b_pos > 0) {
c->frac[--c_pos] = a->frac[--a_pos] + b->frac[--b_pos] + carry;
if(c->frac[c_pos] >= BASE) {
c->frac[c_pos] -= BASE;
carry = 1;
} else {
carry = 0;
}
}
/* Just assign the first few digits of a with considering */
/* the carry obtained so far because b has been exhausted. */
while(a_pos > 0) {
c->frac[--c_pos] = a->frac[--a_pos] + carry;
if(c->frac[c_pos] >= BASE) {
c->frac[c_pos] -= BASE;
carry = 1;
} else {
carry = 0;
}
}
if(c_pos) c->frac[c_pos - 1] += carry;
goto Exit;
Assign_a:
VpAsgn(c, a, 1);
mrv = 0;
Exit:
#ifdef BIGDECIMAL_DEBUG
if(gfDebug) {
VPrint(stdout, "VpAddAbs exit: c=% \n", c);
}
#endif /* BIGDECIMAL_DEBUG */
return mrv;
}
/*
* c = abs(a) - abs(b)
*/
static BDIGIT
VpSubAbs(Real *a, Real *b, Real *c)
{
size_t word_shift;
size_t ap;
size_t bp;
size_t cp;
size_t a_pos;
size_t b_pos, b_pos_with_word_shift;
size_t c_pos;
BDIGIT av, bv, borrow, mrv;
#ifdef BIGDECIMAL_DEBUG
if(gfDebug) {
VPrint(stdout, "VpSubAbs called: a = %\n", a);
VPrint(stdout, " b = %\n", b);
}
#endif /* BIGDECIMAL_DEBUG */
word_shift = VpSetPTR(a, b, c, &ap, &bp, &cp, &av, &bv);
a_pos = ap;
b_pos = bp;
c_pos = cp;
if(word_shift==(size_t)-1L) return 0; /* Overflow */
if(b_pos == (size_t)-1L) goto Assign_a;
if(av >= bv) {
mrv = av - bv;
borrow = 0;
} else {
mrv = 0;
borrow = 1;
}
/* Just assign the values which are the BASE subtracted by */
/* each of the last few digits of the b because the a has no */
/* corresponding digits to be subtracted. */
if(b_pos + word_shift > a_pos) {
while(b_pos + word_shift > a_pos) {
--c_pos;
if(b_pos > 0) {
c->frac[c_pos] = BASE - b->frac[--b_pos] - borrow;
} else {
--word_shift;
c->frac[c_pos] = BASE - borrow;
}
borrow = 1;
}
}
/* Just assign the last few digits of a to c because b has no */
/* corresponding digits to subtract. */
b_pos_with_word_shift = b_pos + word_shift;
while(a_pos > b_pos_with_word_shift) {
c->frac[--c_pos] = a->frac[--a_pos];
}
/* Now perform subtraction until every digits of b will be */
/* exhausted. */
while(b_pos > 0) {
--c_pos;
if(a->frac[--a_pos] < b->frac[--b_pos] + borrow) {
c->frac[c_pos] = BASE + a->frac[a_pos] - b->frac[b_pos] - borrow;
borrow = 1;
} else {
c->frac[c_pos] = a->frac[a_pos] - b->frac[b_pos] - borrow;
borrow = 0;
}
}
/* Just assign the first few digits of a with considering */
/* the borrow obtained so far because b has been exhausted. */
while(a_pos > 0) {
--c_pos;
if(a->frac[--a_pos] < borrow) {
c->frac[c_pos] = BASE + a->frac[a_pos] - borrow;
borrow = 1;
} else {
c->frac[c_pos] = a->frac[a_pos] - borrow;
borrow = 0;
}
}
if(c_pos) c->frac[c_pos - 1] -= borrow;
goto Exit;
Assign_a:
VpAsgn(c, a, 1);
mrv = 0;
Exit:
#ifdef BIGDECIMAL_DEBUG
if(gfDebug) {
VPrint(stdout, "VpSubAbs exit: c=% \n", c);
}
#endif /* BIGDECIMAL_DEBUG */
return mrv;
}
/*
* Note: If(av+bv)>= HALF_BASE,then 1 will be added to the least significant
* digit of c(In case of addition).
* ------------------------- figure of output -----------------------------------
* a = xxxxxxxxxxx
* b = xxxxxxxxxx
* c =xxxxxxxxxxxxxxx
* word_shift = | |
* right_word = | | (Total digits in RHSV)
* left_word = | | (Total digits in LHSV)
* a_pos = |
* b_pos = |
* c_pos = |
*/
static size_t
VpSetPTR(Real *a, Real *b, Real *c, size_t *a_pos, size_t *b_pos, size_t *c_pos, BDIGIT *av, BDIGIT *bv)
{
size_t left_word, right_word, word_shift;
c->frac[0] = 0;
*av = *bv = 0;
word_shift =((a->exponent) -(b->exponent));
left_word = b->Prec + word_shift;
right_word = Max((a->Prec),left_word);
left_word =(c->MaxPrec) - 1; /* -1 ... prepare for round up */
/*
* check if 'round' is needed.
*/
if(right_word > left_word) { /* round ? */
/*---------------------------------
* Actual size of a = xxxxxxAxx
* Actual size of b = xxxBxxxxx
* Max. size of c = xxxxxx
* Round off = |-----|
* c_pos = |
* right_word = |
* a_pos = |
*/
*c_pos = right_word = left_word + 1; /* Set resulting precision */
/* be equal to that of c */
if((a->Prec) >=(c->MaxPrec)) {
/*
* a = xxxxxxAxxx
* c = xxxxxx
* a_pos = |
*/
*a_pos = left_word;
*av = a->frac[*a_pos]; /* av is 'A' shown in above. */
} else {
/*
* a = xxxxxxx
* c = xxxxxxxxxx
* a_pos = |
*/
*a_pos = a->Prec;
}
if((b->Prec + word_shift) >= c->MaxPrec) {
/*
* a = xxxxxxxxx
* b = xxxxxxxBxxx
* c = xxxxxxxxxxx
* b_pos = |
*/
if(c->MaxPrec >=(word_shift + 1)) {
*b_pos = c->MaxPrec - word_shift - 1;
*bv = b->frac[*b_pos];
} else {
*b_pos = -1L;
}
} else {
/*
* a = xxxxxxxxxxxxxxxx
* b = xxxxxx
* c = xxxxxxxxxxxxx
* b_pos = |
*/
*b_pos = b->Prec;
}
} else { /* The MaxPrec of c - 1 > The Prec of a + b */
/*
* a = xxxxxxx
* b = xxxxxx
* c = xxxxxxxxxxx
* c_pos = |
*/
*b_pos = b->Prec;
*a_pos = a->Prec;
*c_pos = right_word + 1;
}
c->Prec = *c_pos;
c->exponent = a->exponent;
if(!AddExponent(c,1)) return (size_t)-1L;
return word_shift;
}
/*
* Return number og significant digits
* c = a * b , Where a = a0a1a2 ... an
* b = b0b1b2 ... bm
* c = c0c1c2 ... cl
* a0 a1 ... an * bm
* a0 a1 ... an * bm-1
* . . .
* . . .
* a0 a1 .... an * b0
* +_____________________________
* c0 c1 c2 ...... cl
* nc <---|
* MaxAB |--------------------|
*/
VP_EXPORT size_t
VpMult(Real *c, Real *a, Real *b)
{
size_t MxIndA, MxIndB, MxIndAB, MxIndC;
size_t ind_c, i, ii, nc;
size_t ind_as, ind_ae, ind_bs, ind_be;
BDIGIT carry;
BDIGIT_DBL s;
Real *w;
#ifdef BIGDECIMAL_DEBUG
if(gfDebug) {
VPrint(stdout, "VpMult(Enter): a=% \n", a);
VPrint(stdout, " b=% \n", b);
}
#endif /* BIGDECIMAL_DEBUG */
if(!VpIsDefOP(c,a,b,3)) return 0; /* No significant digit */
if(VpIsZero(a) || VpIsZero(b)) {
/* at least a or b is zero */
VpSetZero(c,VpGetSign(a)*VpGetSign(b));
return 1; /* 0: 1 significant digit */
}
if(VpIsOne(a)) {
VpAsgn(c, b, VpGetSign(a));
goto Exit;
}
if(VpIsOne(b)) {
VpAsgn(c, a, VpGetSign(b));
goto Exit;
}
if((b->Prec) >(a->Prec)) {
/* Adjust so that digits(a)>digits(b) */
w = a;
a = b;
b = w;
}
w = NULL;
MxIndA = a->Prec - 1;
MxIndB = b->Prec - 1;
MxIndC = c->MaxPrec - 1;
MxIndAB = a->Prec + b->Prec - 1;
if(MxIndC < MxIndAB) { /* The Max. prec. of c < Prec(a)+Prec(b) */
w = c;
c = VpAlloc((size_t)((MxIndAB + 1) * BASE_FIG), "#0");
MxIndC = MxIndAB;
}
/* set LHSV c info */
c->exponent = a->exponent; /* set exponent */
if(!AddExponent(c,b->exponent)) {
if(w) VpFree(c);
return 0;
}
VpSetSign(c,VpGetSign(a)*VpGetSign(b)); /* set sign */
carry = 0;
nc = ind_c = MxIndAB;
memset(c->frac, 0, (nc + 1) * sizeof(BDIGIT)); /* Initialize c */
c->Prec = nc + 1; /* set precision */
for(nc = 0; nc < MxIndAB; ++nc, --ind_c) {
if(nc < MxIndB) { /* The left triangle of the Fig. */
ind_as = MxIndA - nc;
ind_ae = MxIndA;
ind_bs = MxIndB;
ind_be = MxIndB - nc;
} else if(nc <= MxIndA) { /* The middle rectangular of the Fig. */
ind_as = MxIndA - nc;
ind_ae = MxIndA -(nc - MxIndB);
ind_bs = MxIndB;
ind_be = 0;
} else if(nc > MxIndA) { /* The right triangle of the Fig. */
ind_as = 0;
ind_ae = MxIndAB - nc - 1;
ind_bs = MxIndB -(nc - MxIndA);
ind_be = 0;
}
for(i = ind_as; i <= ind_ae; ++i) {
s = (BDIGIT_DBL)a->frac[i] * b->frac[ind_bs--];
carry = (BDIGIT)(s / BASE);
s -= (BDIGIT_DBL)carry * BASE;
c->frac[ind_c] += (BDIGIT)s;
if(c->frac[ind_c] >= BASE) {
s = c->frac[ind_c] / BASE;
carry += (BDIGIT)s;
c->frac[ind_c] -= (BDIGIT)(s * BASE);
}
if(carry) {
ii = ind_c;
while(ii-- > 0) {
c->frac[ii] += carry;
if(c->frac[ii] >= BASE) {
carry = c->frac[ii] / BASE;
c->frac[ii] -= (carry * BASE);
} else {
break;
}
}
}
}
}
if(w != NULL) { /* free work variable */
VpNmlz(c);
VpAsgn(w, c, 1);
VpFree(c);
c = w;
} else {
VpLimitRound(c,0);
}
Exit:
#ifdef BIGDECIMAL_DEBUG
if(gfDebug) {
VPrint(stdout, "VpMult(c=a*b): c=% \n", c);
VPrint(stdout, " a=% \n", a);
VPrint(stdout, " b=% \n", b);
}
#endif /*BIGDECIMAL_DEBUG */
return c->Prec*BASE_FIG;
}
/*
* c = a / b, remainder = r
*/
VP_EXPORT size_t
VpDivd(Real *c, Real *r, Real *a, Real *b)
{
size_t word_a, word_b, word_c, word_r;
size_t i, n, ind_a, ind_b, ind_c, ind_r;
size_t nLoop;
BDIGIT_DBL q, b1, b1p1, b1b2, b1b2p1, r1r2;
BDIGIT borrow, borrow1, borrow2;
BDIGIT_DBL qb;
#ifdef BIGDECIMAL_DEBUG
if(gfDebug) {
VPrint(stdout, " VpDivd(c=a/b) a=% \n", a);
VPrint(stdout, " b=% \n", b);
}
#endif /*BIGDECIMAL_DEBUG */
VpSetNaN(r);
if(!VpIsDefOP(c,a,b,4)) goto Exit;
if(VpIsZero(a)&&VpIsZero(b)) {
VpSetNaN(c);
return VpException(VP_EXCEPTION_NaN,"(VpDivd) 0/0 not defined(NaN)",0);
}
if(VpIsZero(b)) {
VpSetInf(c,VpGetSign(a)*VpGetSign(b));
return VpException(VP_EXCEPTION_ZERODIVIDE,"(VpDivd) Divide by zero",0);
}
if(VpIsZero(a)) {
/* numerator a is zero */
VpSetZero(c,VpGetSign(a)*VpGetSign(b));
VpSetZero(r,VpGetSign(a)*VpGetSign(b));
goto Exit;
}
if(VpIsOne(b)) {
/* divide by one */
VpAsgn(c, a, VpGetSign(b));
VpSetZero(r,VpGetSign(a));
goto Exit;
}
word_a = a->Prec;
word_b = b->Prec;
word_c = c->MaxPrec;
word_r = r->MaxPrec;
ind_c = 0;
ind_r = 1;
if(word_a >= word_r) goto space_error;
r->frac[0] = 0;
while(ind_r <= word_a) {
r->frac[ind_r] = a->frac[ind_r - 1];
++ind_r;
}
while(ind_r < word_r) r->frac[ind_r++] = 0;
while(ind_c < word_c) c->frac[ind_c++] = 0;
/* initial procedure */
b1 = b1p1 = b->frac[0];
if(b->Prec <= 1) {
b1b2p1 = b1b2 = b1p1 * BASE;
} else {
b1p1 = b1 + 1;
b1b2p1 = b1b2 = b1 * BASE + b->frac[1];
if(b->Prec > 2) ++b1b2p1;
}
/* */
/* loop start */
ind_c = word_r - 1;
nLoop = Min(word_c,ind_c);
ind_c = 1;
while(ind_c < nLoop) {
if(r->frac[ind_c] == 0) {
++ind_c;
continue;
}
r1r2 = (BDIGIT_DBL)r->frac[ind_c] * BASE + r->frac[ind_c + 1];
if(r1r2 == b1b2) {
/* The first two word digits is the same */
ind_b = 2;
ind_a = ind_c + 2;
while(ind_b < word_b) {
if(r->frac[ind_a] < b->frac[ind_b]) goto div_b1p1;
if(r->frac[ind_a] > b->frac[ind_b]) break;
++ind_a;
++ind_b;
}
/* The first few word digits of r and b is the same and */
/* the first different word digit of w is greater than that */
/* of b, so quotinet is 1 and just subtract b from r. */
borrow = 0; /* quotient=1, then just r-b */
ind_b = b->Prec - 1;
ind_r = ind_c + ind_b;
if(ind_r >= word_r) goto space_error;
n = ind_b;
for(i = 0; i <= n; ++i) {
if(r->frac[ind_r] < b->frac[ind_b] + borrow) {
r->frac[ind_r] += (BASE - (b->frac[ind_b] + borrow));
borrow = 1;
} else {
r->frac[ind_r] = r->frac[ind_r] - b->frac[ind_b] - borrow;
borrow = 0;
}
--ind_r;
--ind_b;
}
++c->frac[ind_c];
goto carry;
}
/* The first two word digits is not the same, */
/* then compare magnitude, and divide actually. */
if(r1r2 >= b1b2p1) {
q = r1r2 / b1b2p1; /* q == (BDIGIT)q */
c->frac[ind_c] += (BDIGIT)q;
ind_r = b->Prec + ind_c - 1;
goto sub_mult;
}
div_b1p1:
if(ind_c + 1 >= word_c) goto out_side;
q = r1r2 / b1p1; /* q == (BDIGIT)q */
c->frac[ind_c + 1] += (BDIGIT)q;
ind_r = b->Prec + ind_c;
sub_mult:
borrow1 = borrow2 = 0;
ind_b = word_b - 1;
if(ind_r >= word_r) goto space_error;
n = ind_b;
for(i = 0; i <= n; ++i) {
/* now, perform r = r - q * b */
qb = q * b->frac[ind_b];
if (qb < BASE) borrow1 = 0;
else {
borrow1 = (BDIGIT)(qb / BASE);
qb -= (BDIGIT_DBL)borrow1 * BASE; /* get qb < BASE */
}
if(r->frac[ind_r] < qb) {
r->frac[ind_r] += (BDIGIT)(BASE - qb);
borrow2 = borrow2 + borrow1 + 1;
} else {
r->frac[ind_r] -= (BDIGIT)qb;
borrow2 += borrow1;
}
if(borrow2) {
if(r->frac[ind_r - 1] < borrow2) {
r->frac[ind_r - 1] += (BASE - borrow2);
borrow2 = 1;
} else {
r->frac[ind_r - 1] -= borrow2;
borrow2 = 0;
}
}
--ind_r;
--ind_b;
}
r->frac[ind_r] -= borrow2;
carry:
ind_r = ind_c;
while(c->frac[ind_r] >= BASE) {
c->frac[ind_r] -= BASE;
--ind_r;
++c->frac[ind_r];
}
}
/* End of operation, now final arrangement */
out_side:
c->Prec = word_c;
c->exponent = a->exponent;
if(!AddExponent(c,2)) return 0;
if(!AddExponent(c,-(b->exponent))) return 0;
VpSetSign(c,VpGetSign(a)*VpGetSign(b));
VpNmlz(c); /* normalize c */
r->Prec = word_r;
r->exponent = a->exponent;
if(!AddExponent(r,1)) return 0;
VpSetSign(r,VpGetSign(a));
VpNmlz(r); /* normalize r(remainder) */
goto Exit;
space_error:
#ifdef BIGDECIMAL_DEBUG
if(gfDebug) {
printf(" word_a=%lu\n", word_a);
printf(" word_b=%lu\n", word_b);
printf(" word_c=%lu\n", word_c);
printf(" word_r=%lu\n", word_r);
printf(" ind_r =%lu\n", ind_r);
}
#endif /* BIGDECIMAL_DEBUG */
rb_bug("ERROR(VpDivd): space for remainder too small.");
Exit:
#ifdef BIGDECIMAL_DEBUG
if(gfDebug) {
VPrint(stdout, " VpDivd(c=a/b), c=% \n", c);
VPrint(stdout, " r=% \n", r);
}
#endif /* BIGDECIMAL_DEBUG */
return c->Prec*BASE_FIG;
}
/*
* Input a = 00000xxxxxxxx En(5 preceeding zeros)
* Output a = xxxxxxxx En-5
*/
static int
VpNmlz(Real *a)
{
size_t ind_a, i;
if (!VpIsDef(a)) goto NoVal;
if (VpIsZero(a)) goto NoVal;
ind_a = a->Prec;
while (ind_a--) {
if (a->frac[ind_a]) {
a->Prec = ind_a + 1;
i = 0;
while (a->frac[i] == 0) ++i; /* skip the first few zeros */
if (i) {
a->Prec -= i;
if (!AddExponent(a, -(SIGNED_VALUE)i)) return 0;
memmove(&a->frac[0], &a->frac[i], a->Prec*sizeof(BDIGIT));
}
return 1;
}
}
/* a is zero(no non-zero digit) */
VpSetZero(a, VpGetSign(a));
return 0;
NoVal:
a->frac[0] = 0;
a->Prec = 1;
return 0;
}
/*
* VpComp = 0 ... if a=b,
* Pos ... a>b,
* Neg ... a<b.
* 999 ... result undefined(NaN)
*/
VP_EXPORT int
VpComp(Real *a, Real *b)
{
int val;
size_t mx, ind;
int e;
val = 0;
if(VpIsNaN(a)||VpIsNaN(b)) return 999;
if(!VpIsDef(a)) {
if(!VpIsDef(b)) e = a->sign - b->sign;
else e = a->sign;
if(e>0) return 1;
else if(e<0) return -1;
else return 0;
}
if(!VpIsDef(b)) {
e = -b->sign;
if(e>0) return 1;
else return -1;
}
/* Zero check */
if(VpIsZero(a)) {
if(VpIsZero(b)) return 0; /* both zero */
val = -VpGetSign(b);
goto Exit;
}
if(VpIsZero(b)) {
val = VpGetSign(a);
goto Exit;
}
/* compare sign */
if(VpGetSign(a) > VpGetSign(b)) {
val = 1; /* a>b */
goto Exit;
}
if(VpGetSign(a) < VpGetSign(b)) {
val = -1; /* a<b */
goto Exit;
}
/* a and b have same sign, && signe!=0,then compare exponent */
if((a->exponent) >(b->exponent)) {
val = VpGetSign(a);
goto Exit;
}
if((a->exponent) <(b->exponent)) {
val = -VpGetSign(b);
goto Exit;
}
/* a and b have same exponent, then compare significand. */
mx =((a->Prec) <(b->Prec)) ?(a->Prec) :(b->Prec);
ind = 0;
while(ind < mx) {
if((a->frac[ind]) >(b->frac[ind])) {
val = VpGetSign(a);
goto Exit;
}
if((a->frac[ind]) <(b->frac[ind])) {
val = -VpGetSign(b);
goto Exit;
}
++ind;
}
if((a->Prec) >(b->Prec)) {
val = VpGetSign(a);
} else if((a->Prec) <(b->Prec)) {
val = -VpGetSign(b);
}
Exit:
if (val> 1) val = 1;
else if(val<-1) val = -1;
#ifdef BIGDECIMAL_DEBUG
if(gfDebug) {
VPrint(stdout, " VpComp a=%\n", a);
VPrint(stdout, " b=%\n", b);
printf(" ans=%d\n", val);
}
#endif /* BIGDECIMAL_DEBUG */
return (int)val;
}
/*
* cntl_chr ... ASCIIZ Character, print control characters
* Available control codes:
* % ... VP variable. To print '%', use '%%'.
* \n ... new line
* \b ... backspace
* ... tab
* Note: % must must not appear more than once
* a ... VP variable to be printed
*/
#ifdef BIGDECIMAL_DEBUG
VP_EXPORT int
VPrint(FILE *fp, const char *cntl_chr, Real *a)
{
size_t i, j, nc, nd, ZeroSup;
BDIGIT m, e, nn;
/* Check if NaN & Inf. */
if(VpIsNaN(a)) {
fprintf(fp,SZ_NaN);
return 8;
}
if(VpIsPosInf(a)) {
fprintf(fp,SZ_INF);
return 8;
}
if(VpIsNegInf(a)) {
fprintf(fp,SZ_NINF);
return 9;
}
if(VpIsZero(a)) {
fprintf(fp,"0.0");
return 3;
}
j = 0;
nd = nc = 0; /* nd : number of digits in fraction part(every 10 digits, */
/* nd<=10). */
/* nc : number of caracters printed */
ZeroSup = 1; /* Flag not to print the leading zeros as 0.00xxxxEnn */
while(*(cntl_chr + j)) {
if((*(cntl_chr + j) == '%') &&(*(cntl_chr + j + 1) != '%')) {
nc = 0;
if(!VpIsZero(a)) {
if(VpGetSign(a) < 0) {
fprintf(fp, "-");
++nc;
}
nc += fprintf(fp, "0.");
for(i=0; i < a->Prec; ++i) {
m = BASE1;
e = a->frac[i];
while(m) {
nn = e / m;
if((!ZeroSup) || nn) {
nc += fprintf(fp, "%lu", (unsigned long)nn); /* The leading zero(s) */
/* as 0.00xx will not */
/* be printed. */
++nd;
ZeroSup = 0; /* Set to print succeeding zeros */
}
if(nd >= 10) { /* print ' ' after every 10 digits */
nd = 0;
nc += fprintf(fp, " ");
}
e = e - nn * m;
m /= 10;
}
}
nc += fprintf(fp, "E%"PRIdVALUE, VpExponent10(a));
} else {
nc += fprintf(fp, "0.0");
}
} else {
++nc;
if(*(cntl_chr + j) == '\\') {
switch(*(cntl_chr + j + 1)) {
case 'n':
fprintf(fp, "\n");
++j;
break;
case 't':
fprintf(fp, "\t");
++j;
break;
case 'b':
fprintf(fp, "\n");
++j;
break;
default:
fprintf(fp, "%c", *(cntl_chr + j));
break;
}
} else {
fprintf(fp, "%c", *(cntl_chr + j));
if(*(cntl_chr + j) == '%') ++j;
}
}
j++;
}
return (int)nc;
}
#endif
static void
VpFormatSt(char *psz, size_t fFmt)
{
size_t ie, i, nf = 0;
char ch;
if(fFmt<=0) return;
ie = strlen(psz);
for(i = 0; i < ie; ++i) {
ch = psz[i];
if(!ch) break;
if(ISSPACE(ch) || ch=='-' || ch=='+') continue;
if(ch == '.') { nf = 0;continue;}
if(ch == 'E') break;
nf++;
if(nf > fFmt) {
memmove(psz + i + 1, psz + i, ie - i + 1);
++ie;
nf = 0;
psz[i] = ' ';
}
}
}
VP_EXPORT ssize_t
VpExponent10(Real *a)
{
ssize_t ex;
size_t n;
if (!VpHasVal(a)) return 0;
ex = a->exponent * (ssize_t)BASE_FIG;
n = BASE1;
while ((a->frac[0] / n) == 0) {
--ex;
n /= 10;
}
return ex;
}
VP_EXPORT void
VpSzMantissa(Real *a,char *psz)
{
size_t i, n, ZeroSup;
BDIGIT_DBL m, e, nn;
if(VpIsNaN(a)) {
sprintf(psz,SZ_NaN);
return;
}
if(VpIsPosInf(a)) {
sprintf(psz,SZ_INF);
return;
}
if(VpIsNegInf(a)) {
sprintf(psz,SZ_NINF);
return;
}
ZeroSup = 1; /* Flag not to print the leading zeros as 0.00xxxxEnn */
if(!VpIsZero(a)) {
if(VpGetSign(a) < 0) *psz++ = '-';
n = a->Prec;
for (i=0; i < n; ++i) {
m = BASE1;
e = a->frac[i];
while (m) {
nn = e / m;
if((!ZeroSup) || nn) {
sprintf(psz, "%lu", (unsigned long)nn); /* The leading zero(s) */
psz += strlen(psz);
/* as 0.00xx will be ignored. */
ZeroSup = 0; /* Set to print succeeding zeros */
}
e = e - nn * m;
m /= 10;
}
}
*psz = 0;
while(psz[-1]=='0') *(--psz) = 0;
} else {
if(VpIsPosZero(a)) sprintf(psz, "0");
else sprintf(psz, "-0");
}
}
VP_EXPORT int
VpToSpecialString(Real *a,char *psz,int fPlus)
/* fPlus =0:default, =1: set ' ' before digits , =2: set '+' before digits. */
{
if(VpIsNaN(a)) {
sprintf(psz,SZ_NaN);
return 1;
}
if(VpIsPosInf(a)) {
if(fPlus==1) {
*psz++ = ' ';
} else if(fPlus==2) {
*psz++ = '+';
}
sprintf(psz,SZ_INF);
return 1;
}
if(VpIsNegInf(a)) {
sprintf(psz,SZ_NINF);
return 1;
}
if(VpIsZero(a)) {
if(VpIsPosZero(a)) {
if(fPlus==1) sprintf(psz, " 0.0");
else if(fPlus==2) sprintf(psz, "+0.0");
else sprintf(psz, "0.0");
} else sprintf(psz, "-0.0");
return 1;
}
return 0;
}
VP_EXPORT void
VpToString(Real *a, char *psz, size_t fFmt, int fPlus)
/* fPlus =0:default, =1: set ' ' before digits , =2:set '+' before digits. */
{
size_t i, n, ZeroSup;
BDIGIT shift, m, e, nn;
char *pszSav = psz;
ssize_t ex;
if (VpToSpecialString(a, psz, fPlus)) return;
ZeroSup = 1; /* Flag not to print the leading zeros as 0.00xxxxEnn */
if (VpGetSign(a) < 0) *psz++ = '-';
else if (fPlus == 1) *psz++ = ' ';
else if (fPlus == 2) *psz++ = '+';
*psz++ = '0';
*psz++ = '.';
n = a->Prec;
for(i=0;i < n;++i) {
m = BASE1;
e = a->frac[i];
while(m) {
nn = e / m;
if((!ZeroSup) || nn) {