Spec for div changed.
add,sub,mult,div now can specify exact digits number.


git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@4153 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
shigek 2003-07-25 02:26:56 +00:00
Родитель cb5798876e
Коммит aafd6b025c
4 изменённых файлов: 226 добавлений и 268 удалений

Просмотреть файл

@ -44,7 +44,6 @@
/* #define ENABLE_NUMERIC_STRING */
/* #define ENABLE_TRIAL_METHOD */
/* #define ENABLE_BANG_METHOD */
VALUE rb_cBigDecimal;
@ -56,29 +55,30 @@ VALUE rb_cBigDecimal;
#define SAVE(p) PUSH(p->obj);
#define GUARD_OBJ(p,y) {p=y;SAVE(p);}
/* 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,S_INT n);
static unsigned short VpGetException(void);
static void VpSetException(unsigned short f);
static int VpAddAbs(Real *a,Real *b,Real *c);
static int VpSubAbs(Real *a,Real *b,Real *c);
static U_LONG VpSetPTR(Real *a,Real *b,Real *c,U_LONG *a_pos,U_LONG *b_pos,U_LONG *c_pos,U_LONG *av,U_LONG *bv);
static int VpNmlz(Real *a);
static void VpFormatSt(char *psz,S_INT fFmt);
static int VpRdup(Real *m,U_LONG ind_m);
static int VpInternalRound(Real *c,int ixDigit,U_LONG vPrev,U_LONG v);
static U_LONG SkipWhiteChar(char *szVal);
/*
* ================== Ruby Interface part ==========================
*/
static ID coerce;
/*
* **** BigDecimal version ****
*/
static VALUE
BigDecimal_version(VALUE self)
{
return rb_str_new2("1.0.0");
}
/*
* VP routines used in BigDecimal part
*/
static unsigned short VpGetException(void);
static void VpSetException(unsigned short f);
static int VpInternalRound(Real *c,int ixDigit,U_LONG vPrev,U_LONG v);
/*
* **** BigDecimal part ****
*/
/* Following functions borrowed from numeric.c */
static VALUE
coerce_body(VALUE *x)
@ -666,7 +666,7 @@ BigDecimal_mult(VALUE self, VALUE r)
static VALUE
BigDecimal_divide(Real **c, Real **res, Real **div, VALUE self, VALUE r)
/* For c,res = self.div(r): no round operation */
/* For c = self.div(r): with round operation */
{
ENTER(5);
Real *a, *b;
@ -677,7 +677,7 @@ BigDecimal_divide(Real **c, Real **res, Real **div, VALUE self, VALUE r)
if(!b) return DoSomeOne(self,r);
SAVE(b);
*div = b;
mx =(a->MaxPrec + b->MaxPrec) *VpBaseFig();
mx =(a->MaxPrec + b->MaxPrec + 1) * VpBaseFig();
GUARD_OBJ((*c),VpCreateRbObject(mx, "0"));
GUARD_OBJ((*res),VpCreateRbObject((mx+1) * 2 +(VpBaseFig() + 1), "#0"));
VpDivd(*c, *res, a, b);
@ -814,69 +814,57 @@ BigDecimal_divmod(VALUE self, VALUE r)
}
static VALUE
BigDecimal_divmod2(VALUE self, VALUE b, VALUE n)
BigDecimal_div2(VALUE self, VALUE b, VALUE n)
{
ENTER(10);
VALUE obj;
Real *res=NULL;
Real *av=NULL, *bv=NULL, *cv=NULL;
U_LONG mx = (U_LONG)GetPositiveInt(n)+VpBaseFig();
obj = rb_ary_new();
U_LONG ix = (U_LONG)GetPositiveInt(n);
U_LONG mx = (ix+VpBaseFig()*2);
GUARD_OBJ(cv,VpCreateRbObject(mx,"0"));
GUARD_OBJ(av,GetVpValue(self,1));
GUARD_OBJ(bv,GetVpValue(b,1));
mx = cv->MaxPrec+1;
GUARD_OBJ(res,VpCreateRbObject((mx * 2 + 1)*VpBaseFig(), "#0"));
GUARD_OBJ(res,VpCreateRbObject((mx * 2 + 2)*VpBaseFig(), "#0"));
VpDivd(cv,res,av,bv);
obj = rb_ary_push(obj, ToValue(cv));
obj = rb_ary_push(obj, ToValue(res));
return obj;
VpLeftRound(cv,VpGetCompMode(),ix);
return ToValue(cv);
}
static VALUE
BigDecimal_add2(VALUE self, VALUE b, VALUE n)
{
ENTER(5);
Real *av;
Real *bv;
Real *cv;
ENTER(2);
Real *cv;
U_LONG mx = (U_LONG)GetPositiveInt(n);
GUARD_OBJ(cv,VpCreateRbObject(mx,"0"));
GUARD_OBJ(av,GetVpValue(self,1));
GUARD_OBJ(bv,GetVpValue(b,1));
VpAddSub(cv,av,bv,1);
VALUE c = BigDecimal_add(self,b);
GUARD_OBJ(cv,GetVpValue(c,1));
VpLeftRound(cv,VpGetCompMode(),mx);
return ToValue(cv);
}
static VALUE
BigDecimal_sub2(VALUE self, VALUE b, VALUE n)
{
ENTER(5);
Real *av;
Real *bv;
ENTER(2);
Real *cv;
U_LONG mx = (U_LONG)GetPositiveInt(n);
GUARD_OBJ(cv,VpCreateRbObject(mx,"0"));
GUARD_OBJ(av,GetVpValue(self,1));
GUARD_OBJ(bv,GetVpValue(b,1));
VpAddSub(cv,av,bv,-1);
VALUE c = BigDecimal_sub(self,b);
GUARD_OBJ(cv,GetVpValue(c,1));
VpLeftRound(cv,VpGetCompMode(),mx);
return ToValue(cv);
}
static VALUE
BigDecimal_mult2(VALUE self, VALUE b, VALUE n)
{
ENTER(5);
Real *av;
Real *bv;
ENTER(2);
Real *cv;
U_LONG mx = (U_LONG)GetPositiveInt(n);
GUARD_OBJ(cv,VpCreateRbObject(mx,"0"));
GUARD_OBJ(av,GetVpValue(self,1));
GUARD_OBJ(bv,GetVpValue(b,1));
VpMult(cv,av,bv);
VALUE c = BigDecimal_mult(self,b);
GUARD_OBJ(cv,GetVpValue(c,1));
VpLeftRound(cv,VpGetCompMode(),mx);
return ToValue(cv);
}
@ -1119,7 +1107,6 @@ BigDecimal_inspect(VALUE self)
pszAll = ALLOCA_N(char,nc+256);
VpToString(vp, psz1, 10);
sprintf(pszAll,"#<BigDecimal:%lx,'%s',%lu(%lu)>",self,psz1,VpPrec(vp)*VpBaseFig(),VpMaxPrec(vp)*VpBaseFig());
obj = rb_str_new2(pszAll);
return obj;
}
@ -1272,102 +1259,6 @@ BigDecimal_sincos(VALUE self, VALUE nFig)
}
#endif /* ENABLE_TRIAL_METHOD */
#ifdef ENABLE_BANG_METHOD
/**** Following methods are all MUTABLE and not currently activated. ****/
static void
CheckAssign(VALUE x, VALUE y)
{
if(x==y)
rb_fatal("Bad assignment(the same object appears on both LHS and RHS).");
}
static VALUE
BigDecimal_divmod4(VALUE self, VALUE c, VALUE r, VALUE a, VALUE b)
{
ENTER(10);
U_LONG f;
Real *res=NULL;
Real *av=NULL, *bv=NULL, *cv=NULL;
CheckAssign(c,a);
CheckAssign(c,b);
CheckAssign(r,a);
CheckAssign(r,b);
CheckAssign(r,c);
GUARD_OBJ(cv,GetVpValue(c,1));
GUARD_OBJ(av,GetVpValue(a,1));
GUARD_OBJ(bv,GetVpValue(b,1));
GUARD_OBJ(res,GetVpValue(r,1));
f = VpDivd(cv,res,av,bv);
return INT2FIX(f);
}
static VALUE
BigDecimal_assign(VALUE self, VALUE c, VALUE a, VALUE f)
{
ENTER(5);
int v;
Real *av;
Real *cv;
CheckAssign(c,a);
Check_Type(f, T_FIXNUM);
GUARD_OBJ(cv,GetVpValue(c,1));
GUARD_OBJ(av,GetVpValue(a,1));
v = VpAsgn(cv,av,FIX2INT(f));
return INT2NUM(v);
}
static VALUE
BigDecimal_add3(VALUE self, VALUE c, VALUE a, VALUE b)
{
ENTER(5);
Real *av;
Real *bv;
Real *cv;
U_LONG f;
CheckAssign(c,a);
CheckAssign(c,b);
GUARD_OBJ(cv,GetVpValue(c,1));
GUARD_OBJ(av,GetVpValue(a,1));
GUARD_OBJ(bv,GetVpValue(b,1));
f = VpAddSub(cv,av,bv,1);
return INT2NUM(f);
}
static VALUE
BigDecimal_sub3(VALUE self, VALUE c, VALUE a, VALUE b)
{
ENTER(5);
Real *av;
Real *bv;
Real *cv;
U_LONG f;
CheckAssign(c,a);
CheckAssign(c,b);
GUARD_OBJ(cv,GetVpValue(c,1));
GUARD_OBJ(av,GetVpValue(a,1));
GUARD_OBJ(bv,GetVpValue(b,1));
f = VpAddSub(cv,av,bv,-1);
return INT2NUM(f);
}
static VALUE
BigDecimal_mult3(VALUE self, VALUE c, VALUE a, VALUE b)
{
ENTER(5);
Real *av;
Real *bv;
Real *cv;
U_LONG f;
CheckAssign(c,a);
CheckAssign(c,b);
GUARD_OBJ(cv,GetVpValue(c,1));
GUARD_OBJ(av,GetVpValue(a,1));
GUARD_OBJ(bv,GetVpValue(b,1));
f = VpMult(cv,av,bv);
return INT2NUM(f);
}
#endif /* ENABLE_BANG_METHOD */
void
Init_bigdecimal(void)
{
@ -1381,12 +1272,13 @@ Init_bigdecimal(void)
rb_define_global_function("BigDecimal", BigDecimal_global_new, -1);
/* Class methods */
rb_define_singleton_method(rb_cBigDecimal, "mode", BigDecimal_mode, 2);
rb_define_singleton_method(rb_cBigDecimal, "new", BigDecimal_new, -1);
rb_define_singleton_method(rb_cBigDecimal, "mode", BigDecimal_mode, 2);
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, "induced_from",BigDecimal_induced_from, 1);
rb_define_singleton_method(rb_cBigDecimal, "_load", BigDecimal_load, 1);
rb_define_singleton_method(rb_cBigDecimal, "ver", BigDecimal_version, 0);
/* Constants definition */
rb_define_const(rb_cBigDecimal, "BASE", INT2FIX((S_INT)VpBaseVal()));
@ -1421,7 +1313,7 @@ Init_bigdecimal(void)
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_divmod2, 2);
rb_define_method(rb_cBigDecimal, "div",BigDecimal_div2, 2);
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);
@ -1474,14 +1366,6 @@ Init_bigdecimal(void)
rb_define_method(rb_cBigDecimal, "exp", BigDecimal_exp, 1);
rb_define_method(rb_cBigDecimal, "sincos", BigDecimal_sincos, 1);
#endif /* ENABLE_TRIAL_METHOD */
#ifdef ENABLE_BANG_METHOD
rb_define_singleton_method(rb_cBigDecimal, "assign!", BigDecimal_assign, 3);
rb_define_singleton_method(rb_cBigDecimal, "add!", BigDecimal_add3, 3);
rb_define_singleton_method(rb_cBigDecimal, "sub!", BigDecimal_sub3, 3);
rb_define_singleton_method(rb_cBigDecimal, "mult!", BigDecimal_mult3, 3);
rb_define_singleton_method(rb_cBigDecimal, "div!",BigDecimal_divmod4, 4);
#endif /* ENABLE_BANG_METHOD */
}
/*
@ -1514,6 +1398,20 @@ static Real *VpPt5; /* constant 0.5 */
static U_LONG maxnr = 100; /* 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,S_INT n);
static int VpAddAbs(Real *a,Real *b,Real *c);
static int VpSubAbs(Real *a,Real *b,Real *c);
static U_LONG VpSetPTR(Real *a,Real *b,Real *c,U_LONG *a_pos,U_LONG *b_pos,U_LONG *c_pos,U_LONG *av,U_LONG *bv);
static int VpNmlz(Real *a);
static void VpFormatSt(char *psz,S_INT fFmt);
static int VpRdup(Real *m,U_LONG ind_m);
static U_LONG SkipWhiteChar(char *szVal);
#ifdef _DEBUG
static int gnAlloc=0; /* Memory allocation counter */
#endif /* _DEBUG */
@ -1972,8 +1870,8 @@ VpAlloc(U_LONG mx, char *szVal)
if(szVal) {
if(*szVal!='#') {
if(mf) {
mf = (mf + BASE_FIG - 1) / BASE_FIG + 1;
if(mx>mf) {
mf = (mf + BASE_FIG - 1) / BASE_FIG + 2; /* Needs 1 more for div */
if(mx>mf) {
mx = mf;
}
}
@ -1993,20 +1891,20 @@ VpAlloc(U_LONG mx, char *szVal)
return vp;
}
/* Check on Inf & NaN */
if(StrCmp(szVal,"+Infinity")==0 ||
StrCmp(szVal, "Infinity")==0 ) {
if(StrCmp(szVal,SZ_PINF)==0 ||
StrCmp(szVal,SZ_INF)==0 ) {
vp = (Real *) VpMemAlloc(sizeof(Real) + sizeof(U_LONG));
vp->MaxPrec = 1; /* set max precision */
VpSetPosInf(vp);
return vp;
}
if(StrCmp(szVal,"-Infinity")==0) {
if(StrCmp(szVal,SZ_NINF)==0) {
vp = (Real *) VpMemAlloc(sizeof(Real) + sizeof(U_LONG));
vp->MaxPrec = 1; /* set max precision */
VpSetNegInf(vp);
return vp;
}
if(StrCmp(szVal,"NaN")==0) {
if(StrCmp(szVal,SZ_NaN)==0) {
vp = (Real *) VpMemAlloc(sizeof(Real) + sizeof(U_LONG));
vp->MaxPrec = 1; /* set max precision */
VpSetNaN(vp);
@ -3047,15 +2945,15 @@ VPrint(FILE *fp, char *cntl_chr, Real *a)
/* Check if NaN & Inf. */
if(VpIsNaN(a)) {
fprintf(fp,"NaN");
fprintf(fp,SZ_NaN);
return 8;
}
if(VpIsPosInf(a)) {
fprintf(fp,"Infinity");
fprintf(fp,SZ_INF);
return 8;
}
if(VpIsNegInf(a)) {
fprintf(fp,"-Infinity");
fprintf(fp,SZ_NINF);
return 9;
}
if(VpIsZero(a)) {
@ -3188,15 +3086,15 @@ VpSzMantissa(Real *a,char *psz)
U_LONG n, m, e, nn;
if(VpIsNaN(a)) {
sprintf(psz,"NaN");
sprintf(psz,SZ_NaN);
return;
}
if(VpIsPosInf(a)) {
sprintf(psz,"Infinity");
sprintf(psz,SZ_INF);
return;
}
if(VpIsNegInf(a)) {
sprintf(psz,"-Infinity");
sprintf(psz,SZ_NINF);
return;
}
@ -3235,15 +3133,15 @@ VpToString(Real *a,char *psz,int fFmt)
S_LONG ex;
if(VpIsNaN(a)) {
sprintf(psz,"NaN");
sprintf(psz,SZ_NaN);
return;
}
if(VpIsPosInf(a)) {
sprintf(psz,"Infinity");
sprintf(psz,SZ_INF);
return;
}
if(VpIsNegInf(a)) {
sprintf(psz,"-Infinity");
sprintf(psz,SZ_NINF);
return;
}
@ -3732,23 +3630,22 @@ Exit:
*
*/
VP_EXPORT void
VpActiveRound(Real *y, Real *x, int f, int nf)
VpMidRound(Real *y, int f, int nf)
/*
* Round reletively from the decimal point.
* f: rounding mode
* nf: digit location to round from the the decimal point.
*/
{
int n,i,ix,ioffset;
U_LONG v;
U_LONG div;
if(!VpIsDef(x)) {
VpAsgn(y,x,1);
goto Exit;
}
/* First,assign whole value in truncation mode */
VpAsgn(y, x, 1); /* 1 round off,2 round up */
nf += y->exponent*((int)BASE_FIG);
/* ix: x->fraq[ix] contains round position */
ix = (nf + ((int)BASE_FIG))/((int)BASE_FIG)-1;
if(ix<0 || ((U_LONG)ix)>=y->Prec) goto Exit; /* Unable to round */
ix = nf/(int)BASE_FIG;
if(ix<0 || ((U_LONG)ix)>=y->Prec) return; /* Unable to round */
ioffset = nf - ix*((int)BASE_FIG);
memset(y->frac+ix+1, 0, (y->Prec - (ix+1)) * sizeof(U_LONG));
/* VpNmlz(y); */
@ -3765,10 +3662,10 @@ VpActiveRound(Real *y, Real *x, int f, int nf)
if(v>=5) ++div;
break;
case VP_COMP_MODE_CEIL: /* ceil */
if(v && (VpGetSign(x)>0)) ++div;
if(v && (VpGetSign(y)>0)) ++div;
break;
case VP_COMP_MODE_FLOOR: /* floor */
if(v && (VpGetSign(x)<0)) ++div;
if(v && (VpGetSign(y)<0)) ++div;
break;
case VP_COMP_MODE_EVEN: /* Banker's rounding */
if(v>5) ++div;
@ -3789,27 +3686,50 @@ VpActiveRound(Real *y, Real *x, int f, int nf)
VpRdup(y,0);
} else {
VpSetOne(y);
VpSetSign(y,VpGetSign(x));
VpSetSign(y,VpGetSign(y));
}
} else {
y->frac[ix] = div;
VpNmlz(y);
}
}
Exit:
#ifdef _DEBUG
if(gfDebug) {
VPrint(stdout, "VpActiveRound y=%\n", y);
VPrint(stdout, " x=%\n", x);
}
#endif /*_DEBUG */
return;
VP_EXPORT void
VpLeftRound(Real *y, int f, int nf)
/*
* Round from the left hand side of the digits.
*/
{
U_LONG v;
if(!VpIsDef(y)) return; /* Unable to round */
if(VpIsZero(y)) return;
v = y->frac[0];
nf -= VpExponent(y)*BASE_FIG;
while(v=v/10) nf--;
nf += (BASE_FIG-1);
VpMidRound(y,f,nf);
}
VP_EXPORT void
VpActiveRound(Real *y, Real *x, int f, int nf)
{
/* First,assign whole value in truncation mode */
VpAsgn(y, x, 1); /* 1 round off,2 round up */
if(!VpIsDef(y)) return; /* Unable to round */
if(VpIsZero(y)) return;
VpMidRound(y,f,nf);
}
static int
VpInternalRound(Real *c,int ixDigit,U_LONG vPrev,U_LONG v)
{
int f = 0;
if(!VpIsDef(c)) return f; /* Unable to round */
if(VpIsZero(c)) return f;
v /= BASE1;
switch(gfCompMode) {
case VP_COMP_MODE_TRUNCATE:

Просмотреть файл

@ -20,6 +20,14 @@
extern "C" {
#endif
/*
* NaN & Infinity
*/
#define SZ_NaN "NaN"
#define SZ_INF "Infinity"
#define SZ_PINF "+Infinity"
#define SZ_NINF "+Infinity"
/*
* #define VP_EXPORT other than static to let VP_ routines
* be called from outside of this module.
@ -136,6 +144,8 @@ VP_EXPORT void VpDtoV(Real *m,double d);
VP_EXPORT void VpItoV(Real *m,S_INT ival);
VP_EXPORT int VpSqrt(Real *y,Real *x);
VP_EXPORT void VpActiveRound(Real *y,Real *x,int f,int il);
VP_EXPORT void VpMidRound(Real *y, int f, int nf);
VP_EXPORT void VpLeftRound(Real *y, int f, int nf);
VP_EXPORT void VpFrac(Real *y,Real *x);
VP_EXPORT int VpPower(Real *y,Real *x,S_INT n);
VP_EXPORT void VpPi(Real *y);
@ -189,7 +199,7 @@ VP_EXPORT void VpSinCos(Real *psin,Real *pcos,Real *x);
#define VpSetNegInf(a) ((a)->frac[0]=0,(a)->Prec=1,(a)->sign=VP_SIGN_NEGATIVE_INFINITE)
#define VpSetInf(a,s) ( ((s)>0)?VpSetPosInf(a):VpSetNegInf(a) )
#define VpIsOne(a) ((a->Prec==1)&&(a->frac[0]==1)&&(a->exponent==1))
#define VpExponent(a) (a->exponent)
#ifdef _DEBUG
int VpVarCheck(Real * v);
VP_EXPORT int VPrint(FILE *fp,char *cntl_chr,Real *a);

Просмотреть файл

@ -108,13 +108,13 @@ where:<BR>
s: Initial value string.<BR>
n: Maximum number of significant digits of a. n must be a Fixnum object.
If n is omitted or is equal to 0,then the maximum number of significant digits of a is determined from the length of s.
Actual number of digits handled in computations are usually gretaer than n.<BR>
n is useful when performing divisions like
<CODE><PRE>
BigDecimal("1") / BigDecimal("3") # => 0.3333333333 33E0
BigDecimal("1",10) / BigDecimal("3",10) # => 0.3333333333 3333333333 33333333E0
</PRE></CODE>
but the result may differ in future version.
but the resulting digits obtained may differ in future version.
</BLOCKQUOTE>
<LI><B>mode</B></LI><BLOCKQUOTE>
@ -168,14 +168,17 @@ where flag must be one of:
</TABLE>
nil is returned if any argument is illegal.<BR>
The digit location for rounding operation can not be specified by mode method,
use truncate/round/ceil/floor mthods for each instance instead.
use truncate/round/ceil/floor/add/sub/mult/div mthods for each instance instead.
</BLOCKQUOTE>
<LI><B>limit[(n)]</B></LI><BLOCKQUOTE>
Limits the maximum digits that the newly created BigDecimal objects can hold
never exceed n. Returns maximum value before set.
never exceed n+? (Currently,? can not be determined beforehand,but not so big).
This means the rounding operation specified by BigDecimal.mode is
performed if necessary.
limit returns maximum value before set.
Zero,the default value,means no upper limit.<BR>
Except for zero,the limit has more priority than instance methods such as truncate,round,ceil,floor,add,sub,mult,and div. <BR>
mf = BigDecimal::limit(n)<BR>
</BLOCKQUOTE>
@ -230,31 +233,28 @@ For the resulting number of significant digits of c,see <A HREF="#PREC">Resultin
c = a.add(b,n)<BR>
c = a.add(b,n) performs c = a + b.
If n is less than the actual significant digits of a + b,
then c is rounded properly.
then c is rounded properly according to the BigDecimal.limit.
</BLOCKQUOTE>
<LI><B>sub</B></LI><BLOCKQUOTE>
c = a.sub(b,n)<BR>
c = a.sub(b,n) performs c = a - b.
If n is less than the actual significant digits of a - b,
then c is rounded properly.
then c is rounded properly according to the BigDecimal.limit.
</BLOCKQUOTE>
<LI><B>mult</B></LI><BLOCKQUOTE>
c = a.mult(b,n)<BR>
c = a.mult(b,n) performs c = a * b.
If n is less than the actual significant digits of a * b,
then c is rounded properly.
then c is rounded properly according to the BigDecimal.limit.
</BLOCKQUOTE>
<LI><B>div</B></LI><BLOCKQUOTE>
c,r = a.div(b,n)<BR>
c,r = a.div(b,n) performs c = a / b, r is the residue of a / b.
If necessary,the divide operation continues to n digits which c
can hold.
Unlike the divmod method,c is not always an integer.
c is never rounded,and the equation a = c*b + r is always
valid unless c is NaN or Infinity.
c = a.div(b,n)<BR>
c = a.div(b,n) performs c = a / b.
If n is less than the actual significant digits of a / b,
then c is rounded properly according to the BigDecimal.limit.
</BLOCKQUOTE>
<LI><B>%</B></LI><BLOCKQUOTE>
@ -724,32 +724,40 @@ maximum significant digits of both side of the operator.<BR>
For example, c has more than 100 siginificant digits if c is computed as:<BR>
c = 0.1+0.1*10**(-100)<br>
<BR>
As +,-,and * are always exact(no round operation is performed),
As +,-,and * are always exact(no round operation is performed unless BigDecimal.limit is specified),
which means more momories are required to keep computation results.
Division such as c=1.0/3.0 will be rounded.<BR>
But,the division such as c=1.0/3.0 will always be rounded.<BR>
<H3>2. assign,add,sub,mult,div</H3>
The length of the significant digits obtained from +,-,*,/
is always defined by that of right and left side of the operator.
To specify the length of the significant digits by your self,
use methos assign,add,sub,mult,div, or limit(class method).
use methos assign,add,sub,mult,div.
<CODE><PRE>
BigDecimal("2").div(3,12) # 2.0/3.0 => 0.6666666666 67E0
</PRE></CODE>
</BLOCKQUOTE>
<H3>3. truncate,round,ceil,floor</H3>
Using these methods,you can specify rounding location relatively from
decimal point.
<CODE><PRE>
BigDecimal("6.66666666666666").round(12) # => 0.6666666666 667E1
</PRE></CODE>
</BLOCKQUOTE>
<H3>4. Example</H3>
Following example compute the ratio of the circumference of a circle to
its dirmeter(pi=3.14159265358979....) using J.Machin's formula.
<BR><BR>
<CODE><PRE>
#!/usr/local/bin/ruby
#
# pi.rb
# USAGE: ruby pi.rb n
# where n is the number of digits required.
# EX.: ruby pi.rb 1000
#
require "bigdecimal"
#
# Calculates 3.1415.... using J. Machin's formula.
# Calculates 3.1415.... (the number of times that a circle's diameter
# will fit around the circle) using J. Machin's formula.
#
def big_pi(sig) # sig: Number of significant figures
exp = -sig
@ -762,9 +770,9 @@ def big_pi(sig) # sig: Number of significant figures
k = BigDecimal::new("1")
w = BigDecimal::new("1")
t = BigDecimal::new("-80")
while (u.exponent >= exp)
while (u.nonzero? && u.exponent >= exp)
t = t*m25
u,r = t.div(k,sig)
u = t.div(k,sig)
pi = pi + u
k = k+two
end
@ -773,9 +781,9 @@ def big_pi(sig) # sig: Number of significant figures
k = BigDecimal::new("1")
w = BigDecimal::new("1")
t = BigDecimal::new("956")
while (u.exponent >= exp )
t,r = t.div(m57121,sig)
u,r = t.div(k,sig)
while (u.nonzero? && u.exponent >= exp )
t = t.div(m57121,sig)
u = t.div(k,sig)
pi = pi + u
k = k+two
end
@ -783,8 +791,12 @@ def big_pi(sig) # sig: Number of significant figures
end
if $0 == __FILE__
print "PI("+ARGV[0]+"):\n"
p pi(ARGV[0].to_i)
if ARGV.size == 1
print "PI("+ARGV[0]+"):\n"
p big_pi(ARGV[0].to_i)
else
print "TRY: ruby pi.rb 1000 \n"
end
end
</PRE></CODE>

Просмотреть файл

@ -92,8 +92,8 @@ c=a+b
<H3>メソッド一覧</H3>
以下のメソッドが利用可能です。
「有効桁数」とは BigDecimal が精度を保証する桁数です。
ぴったりではありません、若干の余裕を持って計算されます。また、
例えば32ビットのシステムでは10進で4桁毎に計算します。従って、現状では、
ぴったりではありません、若干の余裕を持って計算されます。
また、例えば32ビットのシステムでは10進で4桁毎に計算します。従って、現状では、
内部の「有効桁数」は4の倍数となっています。
<P>
以下のメソッド以外にも、(C ではない) Ruby ソースの形で
@ -123,7 +123,6 @@ BigDecimal("1",10) / BigDecimal("3",10) # => 0.3333333333 3333333333 33333333E0
</PRE></CODE>
ただし、個々の演算における最大有効桁数 n の取り扱いは将来のバージョンで
若干変更される可能性があります。
</BLOCKQUOTE>
<LI><B>mode</B></LI><BLOCKQUOTE>
@ -181,15 +180,23 @@ f = BigDecimal::mode(BigDecimal::COMP_MODE,flag)
戻り値は指定前の flag の値です。
引数に正しくないものが指定された場合は nil が返ります。<BR>
mode メソッドでは丸め操作の位置をユーザが指定することはできません。
丸め操作と位置を自分で制御したい場合は truncate/round/ceil/floor といった
インスタンスメソッドを使用して下さい。
丸め操作と位置を自分で制御したい場合は truncate/round/ceil/floor
add/sub/mult といったインスタンスメソッドを使用して下さい。
</BLOCKQUOTE>
<LI><B>limit([n])</B></LI><BLOCKQUOTE>
生成されるBigDecimalオブジェクトの最大桁数をn桁に制限します。戻り値は
設定する前の値です。設定値のデフォルト値は0で、桁数無制限という意味です。
nを指定しない場合は、現状の最大桁数が返ります。<BR>
生成されるBigDecimalオブジェクトの最大桁数をn桁に制限します。
戻り値は設定する前の値です。設定値のデフォルト値は0で、桁数無制限という意味です。
n を指定しない場合は、現状の最大桁数が返ります。<BR>
計算を続行する間に、数字の桁数が無制限に増えてしまうような場合
limit で予め桁数を制限できます。この場合 BigDecimal.mode で指定された
丸め処理が実行されます。
ただし、実際には n より若干大きい
桁数が確保されます。また、limit による桁数制限は(無制限を除いて)、
インスタンスメソッド (truncate/round/ceil/floor/add/sub/mult) より
優先されるので注意が必要です。<BR>
mf = BigDecimal::limit(n)<BR>
</BLOCKQUOTE>
<LI><B>double_fig</B></LI><BLOCKQUOTE>
Ruby の Float クラスが保持できる有効数字の数を返します。
<CODE><PRE>
@ -240,32 +247,30 @@ c
以下のように使用します。<BR>
c = a.add(b,n)<BR>
c = a + b を最大で n 桁まで計算します。
a + b の精度が n より大きいときは丸められます。
a + b の精度が n より大きいときは BigDecimal.mode で指定された方法で丸められます。
</BLOCKQUOTE>
<LI><B>sub</B></LI><BLOCKQUOTE>
以下のように使用します。<BR>
c = a.sub(b,n)<BR>
c = a - b を最大で n 桁まで計算します。
a - b の精度が n より大きいときは丸められます。
a - b の精度が n より大きいときは BigDecimal.mode で指定された方法で丸められます。
</BLOCKQUOTE>
<LI><B>mult</B></LI><BLOCKQUOTE>
以下のように使用します。<BR>
c = a.mult(b,n)<BR>
c = a * b を最大で n 桁まで計算します。
a * b の精度が n より大きいときは丸められます。
a * b の精度が n より大きいときは BigDecimal.mode で指定された方法で丸められます。
</BLOCKQUOTE>
<LI><B>div</B></LI><BLOCKQUOTE>
以下のように使用します。<BR>
c,r = a.div(b,n)<BR>
c=a/b の計算をします。 r には剰余が代入されます。a/bは
必要ならn 桁まで計算されます。divmod メソッド
と異なり、c は整数とは限りません。
また、 c は丸められることはありません。
a = c*b + r の関係は成立します。
c = a.div(b,n)<BR>
c = a / b を最大で n 桁まで計算します。
a / b の精度が n より大きいときは BigDecimal.mode で指定された方法で丸められます。
</BLOCKQUOTE>
<LI><B>%</B></LI><BLOCKQUOTE>
r = a%b <BR>
a/b の余りを計算します。以下の計算と同じものです。<BR>
@ -708,21 +713,34 @@ c = a op b
加減算の場合は、誤差が出ないだけの精度を持つ c を生成します。例えば
c = 0.1+0.1*10**(-100) のような場合、c の精度は100桁以上の精度を
持つようになります。
<BR>
<BR><BR>
2.次に c = a op b の計算を実行します。<BR><BR>
このように、加減算と乗算での c は必ず「誤差が出ない」だけの精度を
持って生成されます。除算は(a の最大有効桁数)+(b の最大有効桁数)分の最大桁数
持って生成されます(BigDecimal.limit を指定しない場合)。
除算は(a の最大有効桁数)+(b の最大有効桁数)分の最大桁数
を持つ c が生成されますが、c = 1.0/3.0 のような計算で明らかなように、
c の最大精度を超えるところで計算が打ち切られる場合があります。<BR><BR>
いずれにせよ、c の最大精度は a や b より大きくなりますので c が必要とする
メモリー領域は大きくなることに注意して下さい。
<BR><BR>
注意:「+,-,*,/」では結果の精度(有効桁数)を自分で指定できません。
精度をコントロールしたい場合は、以下の add,sub 等のメソッド
を使用します。<BR>
精度をコントロールしたい場合は、以下のインスタンスメソッドを使用します。<BR>
<UL>
<LI>add,sub,mult,div</LI><BLOCKQUOTE>
これらのメソッドは先頭(最左)の数字からの桁数を指定できます。
<CODE><PRE>
BigDecimal("2").div(3,12) # 2.0/3.0 => 0.6666666666 67E0
</PRE></CODE>
</BLOCKQUOTE>
<LI>truncate,round,ceil,floor</LI><BLOCKQUOTE>
これらのメソッドは小数点からの相対位置を指定して桁数を決定します。
<CODE><PRE>
BigDecimal("6.66666666666666").round(12) # => 0.6666666666 667E1
</PRE></CODE>
</BLOCKQUOTE>
</UL>
<H3>自分で精度をコントロールしたい場合</H3>
自分で精度(有効桁数)をコントロールしたい場合は assign、add、sub、mult、div 等のメソッド
自分で精度(有効桁数)をコントロールしたい場合は add、sub、mult、div 等のメソッド
が使用できます。
以下の円周率を計算するプログラム例のように、
求める桁数は自分で指定することができます。
@ -730,16 +748,10 @@ c = a op b
<CODE><PRE>
#!/usr/local/bin/ruby
#
# pi.rb
# USAGE: ruby pi.rb n
# where n is the number of digits required.
# EX.: ruby pi.rb 1000
#
require "bigdecimal"
#
# Calculates 3.1415.... using J. Machin's formula.
# Calculates 3.1415.... (the number of times that a circle's diameter
# will fit around the circle) using J. Machin's formula.
#
def big_pi(sig) # sig: Number of significant figures
exp = -sig
@ -752,9 +764,9 @@ def big_pi(sig) # sig: Number of significant figures
k = BigDecimal::new("1")
w = BigDecimal::new("1")
t = BigDecimal::new("-80")
while (u.exponent >= exp)
while (u.nonzero? && u.exponent >= exp)
t = t*m25
u,r = t.div(k,sig)
u = t.div(k,sig)
pi = pi + u
k = k+two
end
@ -763,9 +775,9 @@ def big_pi(sig) # sig: Number of significant figures
k = BigDecimal::new("1")
w = BigDecimal::new("1")
t = BigDecimal::new("956")
while (u.exponent >= exp )
t,r = t.div(m57121,sig)
u,r = t.div(k,sig)
while (u.nonzero? && u.exponent >= exp )
t = t.div(m57121,sig)
u = t.div(k,sig)
pi = pi + u
k = k+two
end
@ -773,8 +785,12 @@ def big_pi(sig) # sig: Number of significant figures
end
if $0 == __FILE__
print "PI("+ARGV[0]+"):\n"
p pi(ARGV[0].to_i)
if ARGV.size == 1
print "PI("+ARGV[0]+"):\n"
p big_pi(ARGV[0].to_i)
else
print "TRY: ruby pi.rb 1000 \n"
end
end
</PRE></CODE>