From 4cbf09d1fe858f39bd091a6cbe59dcc56ce463aa Mon Sep 17 00:00:00 2001 From: shigek Date: Mon, 28 Jul 2003 02:56:43 +0000 Subject: 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 --- ext/bigdecimal/bigdecimal.c | 46 +++++++++++++++++------------- ext/bigdecimal/bigdecimal.h | 4 +-- ext/bigdecimal/bigdecimal_en.html | 48 +++++++++++++++---------------- ext/bigdecimal/bigdecimal_ja.html | 59 ++++++++++++++++++++++----------------- 4 files changed, 86 insertions(+), 71 deletions(-) (limited to 'ext/bigdecimal') diff --git a/ext/bigdecimal/bigdecimal.c b/ext/bigdecimal/bigdecimal.c index a5d29660fb..a1253e92e7 100644 --- a/ext/bigdecimal/bigdecimal.c +++ b/ext/bigdecimal/bigdecimal.c @@ -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; diff --git a/ext/bigdecimal/bigdecimal.h b/ext/bigdecimal/bigdecimal.h index 62c89dce50..d8fa35d3c5 100644 --- a/ext/bigdecimal/bigdecimal.h +++ b/ext/bigdecimal/bigdecimal.h @@ -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); diff --git a/ext/bigdecimal/bigdecimal_en.html b/ext/bigdecimal/bigdecimal_en.html index ed192fca58..2d86adcc6b 100644 --- a/ext/bigdecimal/bigdecimal_en.html +++ b/ext/bigdecimal/bigdecimal_en.html @@ -156,18 +156,22 @@ Suppose the return value of the mode method is f,then [ROUND error control]

Rounding operation can be controlled as:

-f = BigDecimal::mode(BigDecimal::COMP_MODE,flag) +f = BigDecimal::mode(BigDecimal::ROUND_MODE,flag)
where flag must be one of: - - - - - + + + + + + +
COMP_MODE_TRUNCATEtruncate
COMP_MODE_ROUNDround,default
COMP_MODE_CEILceil
COMP_MODE_FLOORfloor
COMP_MODE_EVENBanker's rounding
ROUND_UPround away from zero.
ROUND_DOWNround towards zero(truncate).
ROUND_HALF_UPround up if the digit >= 5 otherwise truncated(default).
ROUND_HALF_DOWNround up if the digit >= 6 otherwise truncated.
ROUND_HALF_EVENround towards the even neighbor(Banker's rounding). +
ROUND_CEILINGround towards positive infinity(ceil).
ROUND_FLOORround towards negative infinity(floor).
-nil is returned if any argument is illegal.
-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.
+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. @@ -286,8 +290,8 @@ of the target digit can be given.
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).
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).
- 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
 
@@ -304,37 +308,33 @@ of the target digit can be given.
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).
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).
- 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
 
  • round[(n[,b])]
  • c = a.round
    -round a to the nearest 1.
    +round a to the nearest 1(default).
      c = BigDecimal("1.23456").round  #  ==> 1
      c = BigDecimal("-1.23456").round #  ==> -1
     
    +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.
    -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).
    +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).
    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).
     c = BigDecimal::new("1.23456").round(4)   #  ==> 1.2346
     c = BigDecimal::new("15.23456").round(-1) #  ==> 20.0
     
    -If the second optional argument b is given with the non-zero value(default is zero) then -so called Banker's rounding is performed.
    -Suppose the digit p is to be rounded,then:
    - If p<5 then p is truncated
    - If p>5 then p is rounded up
    - 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.
    -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
     
    @@ -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.
    -

    2. assign,add,sub,mult,div

    +

    2. add,sub,mult,div

    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.
      BigDecimal("2").div(3,12) # 2.0/3.0 => 0.6666666666 67E0
     
    diff --git a/ext/bigdecimal/bigdecimal_ja.html b/ext/bigdecimal/bigdecimal_ja.html index f78d63a9b6..31e852bd02 100644 --- a/ext/bigdecimal/bigdecimal_ja.html +++ b/ext/bigdecimal/bigdecimal_ja.html @@ -166,22 +166,26 @@ EXCEPTION_NaN [丸め処理指定]

    計算途中の丸め操作の指定ができます。

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