1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

Bug fix from Javier Goizueta.

ROUND_MODE & round changed(source & docs).


git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@4187 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
shigek 2003-07-28 02:56:43 +00:00
parent b388591ab7
commit 4cbf09d1fe
4 changed files with 86 additions and 71 deletions

View file

@ -919,12 +919,15 @@ static VALUE
BigDecimal_round(int argc, VALUE *argv, VALUE self)
{
ENTER(5);
Real *c, *a;
int iLoc;
int sw;
Real *c, *a;
int iLoc;
U_LONG mx;
VALUE vLoc;
int na = rb_scan_args(argc,argv,"01",&vLoc);
VALUE vLoc;
VALUE vRound;
int sw = VpGetRoundMode();
int na = rb_scan_args(argc,argv,"02",&vLoc,&vRound);
switch(na) {
case 0:
iLoc = 0;
@ -933,12 +936,18 @@ BigDecimal_round(int argc, VALUE *argv, VALUE self)
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 = VpSetRoundMode(FIX2INT(vRound));
break;
}
GUARD_OBJ(a,GetVpValue(self,1));
mx = a->Prec *(VpBaseFig() + 1);
GUARD_OBJ(c,VpCreateRbObject(mx, "0"));
VpActiveRound(c,a,VpGetRoundMode(),iLoc);
VpActiveRound(c,a,sw,iLoc);
return ToValue(c);
}
@ -1291,7 +1300,7 @@ Init_bigdecimal(void)
rb_define_const(rb_cBigDecimal, "ROUND_HALF_DOWN",INT2FIX(VP_ROUND_HALF_DOWN));
rb_define_const(rb_cBigDecimal, "ROUND_CEILING",INT2FIX(VP_ROUND_CEIL));
rb_define_const(rb_cBigDecimal, "ROUND_FLOOR",INT2FIX(VP_ROUND_FLOOR));
rb_define_const(rb_cBigDecimal, "ROUND_EVEN",INT2FIX(VP_ROUND_EVEN));
rb_define_const(rb_cBigDecimal, "ROUND_HALF_EVEN",INT2FIX(VP_ROUND_HALF_EVEN));
/* Constants for sign value */
rb_define_const(rb_cBigDecimal, "SIGN_NaN",INT2FIX(VP_SIGN_NaN));
@ -1484,13 +1493,12 @@ VpGetRoundMode(void)
VP_EXPORT unsigned long
VpSetRoundMode(unsigned long n)
{
unsigned long s = gfRoundMode;
if(n!=VP_ROUND_UP && n!=VP_ROUND_DOWN &&
n!=VP_ROUND_HALF_UP && n!=VP_ROUND_HALF_DOWN &&
n!=VP_ROUND_CEIL && n!=VP_ROUND_FLOOR &&
n!=VP_ROUND_EVEN) return s;
gfRoundMode = n;
return s;
if(n==VP_ROUND_UP || n!=VP_ROUND_DOWN ||
n==VP_ROUND_HALF_UP || n!=VP_ROUND_HALF_DOWN ||
n==VP_ROUND_CEIL || n!=VP_ROUND_FLOOR ||
n==VP_ROUND_HALF_EVEN
) gfRoundMode = n;
return gfRoundMode;
}
/*
@ -3212,7 +3220,7 @@ VpCtoV(Real *a, char *int_chr, U_LONG ni, char *frac, U_LONG nf, char *exp_chr,
++me;
}
while(i < me) {
es = e*BASE_FIG;
es = e*((S_INT)BASE_FIG);
e = e * 10 + exp_chr[i] - '0';
if(es>e*((S_INT)BASE_FIG)) {
return VpException(VP_EXCEPTION_INFINITY,"Exponent overflow",0);
@ -3247,8 +3255,8 @@ VpCtoV(Real *a, char *int_chr, U_LONG ni, char *frac, U_LONG nf, char *exp_chr,
while(ef) {
if(e>=0) eb = e;
else eb = -e;
ef = eb / BASE_FIG;
ef = eb - ef * BASE_FIG;
ef = eb / ((S_INT)BASE_FIG);
ef = eb - ef * ((S_INT)BASE_FIG);
if(ef) {
++j; /* Means to add one more preceeding zero */
++e;
@ -3669,7 +3677,7 @@ VpMidRound(Real *y, int f, int nf)
case VP_ROUND_FLOOR: /* floor */
if(v && (VpGetSign(y)<0)) ++div;
break;
case VP_ROUND_EVEN: /* Banker's rounding */
case VP_ROUND_HALF_EVEN: /* Banker's rounding */
if(v>5) ++div;
else if(v==5) {
if(i==(BASE_FIG-1)) {
@ -3751,7 +3759,7 @@ VpInternalRound(Real *c,int ixDigit,U_LONG vPrev,U_LONG v)
case VP_ROUND_FLOOR: /* floor */
if(v && (VpGetSign(c)<0)) f = 1;
break;
case VP_ROUND_EVEN: /* Banker's rounding */
case VP_ROUND_HALF_EVEN: /* Banker's rounding */
if(v>5) f = 1;
else if(v==5 && vPrev%2) f = 1;
break;

View file

@ -59,7 +59,7 @@ extern "C" {
#define VP_ROUND_HALF_DOWN 4
#define VP_ROUND_CEIL 5
#define VP_ROUND_FLOOR 6
#define VP_ROUND_EVEN 7
#define VP_ROUND_HALF_EVEN 7
#define VP_SIGN_NaN 0 /* NaN */
#define VP_SIGN_POSITIVE_ZERO 1 /* Positive zero */
@ -121,7 +121,7 @@ VP_EXPORT double VpGetDoubleNegZero(void);
VP_EXPORT U_LONG VpGetPrecLimit(void);
VP_EXPORT U_LONG VpSetPrecLimit(U_LONG n);
/* Computation mode */
/* Round mode */
VP_EXPORT unsigned long VpGetRoundMode(void);
VP_EXPORT unsigned long VpSetRoundMode(unsigned long n);

View file

@ -156,18 +156,22 @@ Suppose the return value of the mode method is f,then
<B>[ROUND error control]</B><P>
Rounding operation can be controlled as:
<BLOCKQUOTE>
f = BigDecimal::mode(BigDecimal::COMP_MODE,flag)
f = BigDecimal::mode(BigDecimal::ROUND_MODE,flag)
</BLOCKQUOTE>
where flag must be one of:
<TABLE>
<TR><TD>COMP_MODE_TRUNCATE</TD><TD>truncate</TD></TR>
<TR><TD>COMP_MODE_ROUND</TD><TD>round,default</TD></TR>
<TR><TD>COMP_MODE_CEIL</TD><TD>ceil</TD></TR>
<TR><TD>COMP_MODE_FLOOR</TD><TD>floor</TD></TR>
<TR><TD>COMP_MODE_EVEN</TD><TD>Banker's rounding</TD></TR>
<TR><TD>ROUND_UP</TD><TD>round away from zero.</TD></TR>
<TR><TD>ROUND_DOWN</TD><TD>round towards zero(truncate).</TD></TR>
<TR><TD>ROUND_HALF_UP</TD><TD>round up if the digit &gt;= 5 otherwise truncated(default).</TD></TR>
<TR><TD>ROUND_HALF_DOWN</TD><TD>round up if the digit &gt;= 6 otherwise truncated.</TD></TR>
<TR><TD>ROUND_HALF_EVEN</TD><TD>round towards the even neighbor(Banker's rounding).
<TR><TD>ROUND_CEILING</TD><TD>round towards positive infinity(ceil).</TD></TR>
<TR><TD>ROUND_FLOOR</TD><TD>round towards negative infinity(floor).</TD></TR>
</TABLE>
nil is returned if any argument is illegal.<BR>
The digit location for rounding operation can not be specified by mode method,
New rounding mode is returned,nil is returned if any argument is not an integer.
Bad specification is ignored.<BR>
The digit location for rounding operation can not be specified by this mode method,
use truncate/round/ceil/floor/add/sub/mult/div mthods for each instance instead.
</BLOCKQUOTE>
@ -286,8 +290,8 @@ of the target digit can be given.<BR>
If n> 0,then the (n+1)th digit counted from the decimal point in fraction part is processed(resulting number of fraction part digits is less than or equal to n).<BR>
If n<0,then the n-th digit counted from the decimal point in integer part is processed(at least n 0's are placed from the decimal point to left).
<CODE><PRE>
c = BigDecimal::new("1.23456").floor(4) # ==> 1.2345
c = BigDecimal::new("15.23456").floor(-1) # ==> 10.0
c = BigDecimal("1.23456").floor(4) # ==> 1.2345
c = BigDecimal("15.23456").floor(-1) # ==> 10.0
</PRE></CODE>
</BLOCKQUOTE>
@ -304,37 +308,33 @@ of the target digit can be given.<BR>
If n>0,then the (n+1)th digit counted from the decimal point in fraction part is processed(resulting number of fraction part digits is less than or equal to n).<BR>
If n<0,then the n-th digit counted from the decimal point in integer part is processed(at least n 0's are placed from the decimal point to left).
<CODE><PRE>
c = BigDecimal::new("1.23456").ceil(4) # ==> 1.2346
c = BigDecimal::new("15.23456").ceil(-1) # ==> 20.0
c = BigDecimal("1.23456").ceil(4) # ==> 1.2346
c = BigDecimal("15.23456").ceil(-1) # ==> 20.0
</PRE></CODE>
</BLOCKQUOTE>
<LI><B>round[(n[,b])]</B></LI><BLOCKQUOTE>
c = a.round<BR>
round a to the nearest 1<>D<BR>
round a to the nearest 1(default)<EFBFBD>D<BR>
<CODE><PRE>
c = BigDecimal("1.23456").round # ==> 1
c = BigDecimal("-1.23456").round # ==> -1
</PRE></CODE>
The rounding operation changes according to BigDecimal::mode(BigDecimal::ROUND_MODE,flag) if specified.
As shown in the following example,an optional integer argument (n) specifying the position
of the target digit can be given.<BR>
If n>0,then the (n+1)th digit counted from the decimal point in fraction part is processed(resulting number of fraction part digits is less than or equal to n).<BR>
If n>0,then the (n+1)th digit counted from the decimal point in fraction part is processed(resulting number of fraction part digits is less than or equal to n).<BR>
If n<0,then the n-th digit counted from the decimal point in integer part is processed(at least n 0's are placed from the decimal point to left).
<CODE><PRE>
c = BigDecimal::new("1.23456").round(4) # ==> 1.2346
c = BigDecimal::new("15.23456").round(-1) # ==> 20.0
</PRE></CODE>
If the second optional argument b is given with the non-zero value(default is zero) then
so called Banker's rounding is performed.<BR>
Suppose the digit p is to be rounded,then:<BR>
If p<5 then p is truncated<BR>
If p>5 then p is rounded up<BR>
If p is 5 then round up operation is taken only when the left hand side digit of p is odd.
Rounding operation can be specified by setting the second optional argument b with the valid ROUND_MODE.<BR>
<CODE><PRE>
c = BigDecimal::new("1.23456").round(3,1) # ==> 1.234
c = BigDecimal::new("1.23356").round(3,1) # ==> 1.234
c = BigDecimal::new("1.23456").round(3,BigDecimal::ROUND_HALF_EVEN) # ==> 1.234
c = BigDecimal::new("1.23356").round(3,BigDecimal::ROUND_HALF_EVEN) # ==> 1.234
</PRE></CODE>
</BLOCKQUOTE>
@ -728,11 +728,11 @@ As +,-,and * are always exact(no round operation is performed unless BigDecimal.
which means more momories are required to keep computation results.
But,the division such as c=1.0/3.0 will always be rounded.<BR>
<H3>2. assign,add,sub,mult,div</H3>
<H3>2. 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.
use methos add,sub,mult,div.
<CODE><PRE>
BigDecimal("2").div(3,12) # 2.0/3.0 => 0.6666666666 67E0
</PRE></CODE>

View file

@ -166,22 +166,26 @@ EXCEPTION_NaN
<B>[丸め処理指定]</B><P>
計算途中の丸め操作の指定ができます。
<BLOCKQUOTE>
f = BigDecimal::mode(BigDecimal::COMP_MODE,flag)
f = BigDecimal::mode(BigDecimal::ROUND_MODE,flag)
</BLOCKQUOTE>
の形式で指定します。<BR>
ここで、flag は以下(括弧内は対応するインスタンスメソッド)の一つを指定します。
<TABLE>
<TR><TD>COMP_MODE_TRUNCATE</TD><TD>全て切り捨てます(truncate)。</TD></TR>
<TR><TD>COMP_MODE_ROUND</TD><TD>四捨五入します(round、デフォルト)。</TD></TR>
<TR><TD>COMP_MODE_CEIL</TD><TD>数値の大きい方に繰り上げます(ceil)。</TD></TR>
<TR><TD>COMP_MODE_FLOOR</TD><TD>数値の小さい方に繰り下げます(floor)。</TD></TR>
<TR><TD>COMP_MODE_EVEN</TD><TD>四捨六入します。5の時は上位1桁が奇数の時のみ繰り上げます(Banker's rounding)。</TD></TR>
<TR><TD>ROUND_UP</TD><TD>全て切り上げます。</TD></TR>
<TR><TD>ROUND_DOWN</TD><TD>全て切り捨てます(truncate)。</TD></TR>
<TR><TD>ROUND_HALF_UP</TD><TD>四捨五入します(デフォルト)。</TD></TR>
<TR><TD>ROUND_HALF_DOWN</TD><TD>五捨六入します。</TD></TR>
<TR><TD>ROUND_HALF_EVEN</TD><TD>四捨六入します。5の時は上位1桁が奇数の時のみ繰り上げます(Banker's rounding)。</TD></TR>
<TR><TD>ROUND_CEILING</TD><TD>数値の大きい方に繰り上げます(ceil)。</TD></TR>
<TR><TD>ROUND_FLOOR</TD><TD>数値の小さい方に繰り下げます(floor)。</TD></TR>
</TABLE>
戻り値は指定前の flag の値です。
引数に正しくないものが指定された場合は nil が返ります。<BR>
戻り値は指定後の flag の値です。
引数に数値以外が指定された場合は nil が返ります。正しくない ROUND_MODE が指定されたときは
無視され、現状の ROUND_MODE が返ります。<BR>
mode メソッドでは丸め操作の位置をユーザが指定することはできません。
丸め操作と位置を自分で制御したい場合は truncate/round/ceil/floor や
add/sub/mult といったインスタンスメソッドを使用して下さい。
add/sub/mult/div といったインスタンスメソッドを使用して下さい。
</BLOCKQUOTE>
<LI><B>limit([n])</B></LI><BLOCKQUOTE>
生成されるBigDecimalオブジェクトの最大桁数をn桁に制限します。
@ -192,7 +196,7 @@ n
丸め処理が実行されます。
ただし、実際には n より若干大きい
桁数が確保されます。また、limit による桁数制限は(無制限を除いて)、
インスタンスメソッド (truncate/round/ceil/floor/add/sub/mult) より
インスタンスメソッド (truncate/round/ceil/floor/add/sub/mult/div) より
優先されるので注意が必要です。<BR>
mf = BigDecimal::limit(n)<BR>
</BLOCKQUOTE>
@ -296,8 +300,8 @@ c = BigDecimal("-1.23456").floor # ==> -2
n>=0 なら、小数点以下 n+1 位の数字を操作します(少数点以下を、最大 n 桁にします)。<BR>
n が負のときは小数点以上 n 桁目を操作します(小数点位置から左に少なくとも n 個の 0 が並びます)。<BR>
<CODE><PRE>
c = BigDecimal::new("1.23456").floor(4) # ==> 1.2345
c = BigDecimal::new("15.23456").floor(-1) # ==> 10.0
c = BigDecimal("1.23456").floor(4) # ==> 1.2345
c = BigDecimal("15.23456").floor(-1) # ==> 10.0
</PRE></CODE>
</BLOCKQUOTE>
@ -313,32 +317,35 @@ c = BigDecimal("-1.23456").ceil # ==> -1
n>=0 なら、小数点以下 n+1 位の数字を操作します(少数点以下を、最大 n 桁にします)。<BR>
n が負のときは小数点以上 n 桁目をを操作します(小数点位置から左に少なくとも n 個の 0 が並びます)。<BR>
<CODE><PRE>
c = BigDecimal::new("1.23456").ceil(4) # ==> 1.2346
c = BigDecimal::new("15.23456").ceil(-1) # ==> 20.0
c = BigDecimal("1.23456").ceil(4) # ==> 1.2346
c = BigDecimal("15.23456").ceil(-1) # ==> 20.0
</PRE></CODE>
</BLOCKQUOTE>
<LI><B>round[(n[,b])]</B></LI><BLOCKQUOTE>
c = a.round<BR>
小数点以下第一位の数を四捨五入して整数BigDecimal 値)にします。<BR>
クラスメソッド BigDecimal::mode(BigDecimal::ROUND_MODE,flag) で指定した
ROUND_MODE に従って丸め操作を実行します。
BigDecimal::mode(BigDecimal::ROUND_MODE,flag) で何も指定せず、かつ、引数
を指定しない場合は「小数点以下第一位の数を四捨五入して整数BigDecimal 値)」にします。<BR>
<CODE><PRE>
c = BigDecimal("1.23456").round # ==> 1
c = BigDecimal("-1.23456").round # ==> -1
</PRE></CODE>
以下のように引数を与えて、小数点以下 n+1 位の数字を操作することもできます。<BR>
n が正の時は、小数点以下 n+1 位の数字を四捨五入します(少数点以下を、最大 n 桁にします)。<BR>
n が負のときは小数点以上 n 桁目をを操作します(小数点位置から左に少なくとも n 個の 0 が並びます)。
n が正の時は、小数点以下 n+1 位の数字を丸めます(少数点以下を、最大 n 桁にします)。<BR>
n が負のときは小数点以上 n 桁目を丸めます(小数点位置から左に少なくとも n 個の 0 が並びます)。
<CODE><PRE>
c = BigDecimal::new("1.23456").round(4) # ==> 1.2346
c = BigDecimal::new("15.23456").round(-1) # ==> 20.0
c = BigDecimal("1.23456").round(4) # ==> 1.2346
c = BigDecimal("15.23456").round(-1) # ==> 20.0
</PRE></CODE>
2番目の引数(デフォルトは 0にゼロ以外を指定すると、いわゆる Banker's rounding になります。<BR>
Banker's rounding とは、四捨五入する数字を p として、p &lt; 5 なら切り捨て p &gt; 5 なら切り上げ、
p がちょうど5のときだけは切り上げ先の数字+1が偶数になるときだけ切り上げます。
番目の引数を指定すると、BigDecimal#mode の指定を無視して、指定された方法で
丸め操作を実行します。
<CODE><PRE>
c = BigDecimal::new("1.23456").round(3,1) # ==> 1.234
c = BigDecimal::new("1.23356").round(3,1) # ==> 1.234
c = BigDecimal("1.23456").round(3,BigDecimal::ROUND_HALF_EVEN) # ==> 1.234
c = BigDecimal("1.23356").round(3,BigDecimal::ROUND_HALF_EVEN) # ==> 1.234
</PRE></CODE>
</BLOCKQUOTE>
@ -349,8 +356,8 @@ c = a.truncate<BR>
n が正の時は、小数点以下 n+1 位の数字を切り捨てます(少数点以下を、最大 n 桁にします)。
n が負のときは小数点以上 n 桁目をを操作します(小数点位置から左に少なくとも n 個の 0 が並びます)。<BR>
<CODE><PRE>
c = BigDecimal::new("1.23456").truncate(4) # ==> 1.2345
c = BigDecimal::new("15.23456").truncate(-1) # ==> 10.0
c = BigDecimal("1.23456").truncate(4) # ==> 1.2345
c = BigDecimal("15.23456").truncate(-1) # ==> 10.0
</PRE></CODE>
</BLOCKQUOTE>