[前][次][番号順一覧][スレッド一覧]

ruby-changes:17292

From: mrkn <ko1@a...>
Date: Sun, 19 Sep 2010 06:06:17 +0900 (JST)
Subject: [ruby-changes:17292] Ruby:r29295 (trunk): * ext/bigdecimal/bigdecimal.c (check_rounding_mode): added for

mrkn	2010-09-19 06:06:08 +0900 (Sun, 19 Sep 2010)

  New Revision: 29295

  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=29295

  Log:
    * ext/bigdecimal/bigdecimal.c (check_rounding_mode): added for
      converting symbol to rounding mode number.
    * ext/bigdecimal/bigdecimal.c (BigDecimal_mode, BigDecimal_round):
      support to specify rounding mode by symbol.
    * test/bigdecimal/test_bigdecimal.rb (test_mode, test_round):
      add tests for avobe changes.

  Modified files:
    trunk/ChangeLog
    trunk/ext/bigdecimal/bigdecimal.c
    trunk/test/bigdecimal/test_bigdecimal.rb

Index: ChangeLog
===================================================================
--- ChangeLog	(revision 29294)
+++ ChangeLog	(revision 29295)
@@ -1,3 +1,14 @@
+Sun Sep 19 06:06:07 2010  Kenta Murata  <mrkn@m...>
+
+	* ext/bigdecimal/bigdecimal.c (check_rounding_mode): added for
+	  converting symbol to rounding mode number.
+
+	* ext/bigdecimal/bigdecimal.c (BigDecimal_mode, BigDecimal_round):
+	  support to specify rounding mode by symbol.
+
+	* test/bigdecimal/test_bigdecimal.rb (test_mode, test_round):
+	  add tests for avobe changes.
+
 Sun Sep 19 05:14:35 2010  Kenta Murata  <mrkn@m...>
 
 	* ext/bigdecimal/bigdecimal.c: fix rounding algorithms for half-down
Index: ext/bigdecimal/bigdecimal.c
===================================================================
--- ext/bigdecimal/bigdecimal.c	(revision 29294)
+++ ext/bigdecimal/bigdecimal.c	(revision 29295)
@@ -36,6 +36,18 @@
 static ID id_BigDecimal_rounding_mode;
 static ID id_BigDecimal_precision_limit;
 
+static ID id_up;
+static ID id_down;
+static ID id_truncate;
+static ID id_half_up;
+static ID id_default;
+static ID id_half_down;
+static ID id_half_even;
+static ID id_banker;
+static ID id_ceiling;
+static ID id_ceil;
+static ID id_floor;
+
 /* MACRO's to guard objects from GC by keeping them in stack */
 #define ENTER(n) volatile VALUE vStack[n];int iStack=0
 #define PUSH(x)  vStack[iStack++] = (unsigned long)(x);
@@ -277,44 +289,80 @@
     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 towards the nearest neighbor, unless both neighbors are equidistant, in which case round away from zero. (default)
-  * ROUND_HALF_DOWN:: round towards the nearest neighbor, unless both neighbors are equidistant, in which case round towards zero.
-  * ROUND_HALF_EVEN:: round towards the nearest neighbor, unless both neighbors are equidistant, in which case round towards the even neighbor (Banker's rounding)
-  * ROUND_CEILING:: round towards positive infinity (ceil)
-  * ROUND_FLOOR:: round towards negative infinity (floor)
-  *
-  */
+static unsigned short
+check_rounding_mode(VALUE const v)
+{
+    unsigned short sw;
+    ID id;
+    switch (TYPE(v)) {
+      case T_SYMBOL:
+	id = SYM2ID(v);
+	if (id == id_up)
+	    return VP_ROUND_UP;
+	if (id == id_down || id == id_truncate)
+	    return VP_ROUND_DOWN;
+	if (id == id_half_up || id == id_default)
+	    return VP_ROUND_HALF_UP;
+	if (id == id_half_down)
+	    return VP_ROUND_HALF_DOWN;
+	if (id == id_half_even || id == id_banker)
+	    return VP_ROUND_HALF_EVEN;
+	if (id == id_ceiling || id == id_ceil)
+	    return VP_ROUND_CEIL;
+	if (id == id_floor)
+	    return VP_ROUND_FLOOR;
+	break;
+
+      default:
+	break;
+    }
+
+    Check_Type(v, T_FIXNUM);
+    sw = (unsigned short)FIX2UINT(v);
+    if (!VpIsRoundMode(sw)) {
+	rb_raise(rb_eTypeError, "invalid rounding mode");
+    }
+    return sw;
+}
+
+/* call-seq:
+ * BigDecimal.mode(mode, value)
+ *
+ * Controls handling of arithmetic exceptions and rounding. If no value
+ * is supplied, the current value is returned.
+ *
+ * Six values of the mode parameter control the handling of arithmetic
+ * exceptions:
+ *
+ * BigDecimal::EXCEPTION_NaN
+ * BigDecimal::EXCEPTION_INFINITY
+ * BigDecimal::EXCEPTION_UNDERFLOW
+ * BigDecimal::EXCEPTION_OVERFLOW
+ * BigDecimal::EXCEPTION_ZERODIVIDE
+ * BigDecimal::EXCEPTION_ALL
+ *
+ * For each mode parameter above, if the value set is false, computation
+ * continues after an arithmetic exception of the appropriate type.
+ * When computation continues, results are as follows:
+ *
+ * EXCEPTION_NaN:: NaN
+ * EXCEPTION_INFINITY:: +infinity or -infinity
+ * EXCEPTION_UNDERFLOW:: 0
+ * EXCEPTION_OVERFLOW:: +infinity or -infinity
+ * EXCEPTION_ZERODIVIDE:: +infinity or -infinity
+ *
+ * One value of the mode parameter controls the rounding of numeric values:
+ * BigDecimal::ROUND_MODE. The values it can take are:
+ *
+ * ROUND_UP, :up:: round away from zero
+ * ROUND_DOWN, :down, :truncate:: round towards zero (truncate)
+ * ROUND_HALF_UP, :half_up, :default:: round towards the nearest neighbor, unless both neighbors are equidistant, in which case round away from zero. (default)
+ * ROUND_HALF_DOWN, :half_down:: round towards the nearest neighbor, unless both neighbors are equidistant, in which case round towards zero.
+ * ROUND_HALF_EVEN, :half_even, :banker:: round towards the nearest neighbor, unless both neighbors are equidistant, in which case round towards the even neighbor (Banker's rounding)
+ * ROUND_CEILING, :ceiling, :ceil:: round towards positive infinity (ceil)
+ * ROUND_FLOOR, :floor:: round towards negative infinity (floor)
+ *
+ */
 static VALUE
 BigDecimal_mode(int argc, VALUE *argv, VALUE self)
 {
@@ -357,17 +405,14 @@
         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);
+    if (VP_ROUND_MODE == f) {
+	/* Rounding mode setting */
+	unsigned short sw;
+	fo = VpGetRoundMode();
+	if (NIL_P(val)) return INT2FIX(fo);
+	sw = check_rounding_mode(val);
+	fo = VpSetRoundMode(sw);
+	return INT2FIX(fo);
     }
     rb_raise(rb_eTypeError, "first argument for BigDecimal#mode invalid");
     return Qnil;
@@ -1252,7 +1297,7 @@
 }
 
 /* call-seq:
- * round(n,mode)
+ * round(n, mode)
  *
  * Round to the nearest 1 (by default), returning the result as a BigDecimal.
  *
@@ -1285,8 +1330,7 @@
 
     unsigned short sw = VpGetRoundMode();
 
-    int na = rb_scan_args(argc,argv,"02",&vLoc,&vRound);
-    switch(na) {
+    switch (rb_scan_args(argc, argv, "02", &vLoc, &vRound)) {
     case 0:
         iLoc = 0;
         break;
@@ -1295,15 +1339,10 @@
         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;
+	Check_Type(vLoc, T_FIXNUM);
+	iLoc = FIX2INT(vLoc);
+	sw = check_rounding_mode(vRound);
+	break;
     }
 
     pl = VpSetPrecLimit(0);
@@ -2140,6 +2179,18 @@
     id_BigDecimal_exception_mode = rb_intern_const("BigDecimal.exception_mode");
     id_BigDecimal_rounding_mode = rb_intern_const("BigDecimal.rounding_mode");
     id_BigDecimal_precision_limit = rb_intern_const("BigDecimal.precision_limit");
+
+    id_up = rb_intern_const("up");
+    id_down = rb_intern_const("down");
+    id_truncate = rb_intern_const("truncate");
+    id_half_up = rb_intern_const("half_up");
+    id_default = rb_intern_const("default");
+    id_half_down = rb_intern_const("half_down");
+    id_half_even = rb_intern_const("half_even");
+    id_banker = rb_intern_const("banker");
+    id_ceiling = rb_intern_const("ceiling");
+    id_ceil = rb_intern_const("ceil");
+    id_floor = rb_intern_const("floor");
 }
 
 /*
Index: test/bigdecimal/test_bigdecimal.rb
===================================================================
--- test/bigdecimal/test_bigdecimal.rb	(revision 29294)
+++ test/bigdecimal/test_bigdecimal.rb	(revision 29295)
@@ -5,6 +5,20 @@
 class TestBigDecimal < Test::Unit::TestCase
   include TestBigDecimalBase
 
+  ROUNDING_MODE_MAP = [
+    [ BigDecimal::ROUND_UP,        :up],
+    [ BigDecimal::ROUND_DOWN,      :down],
+    [ BigDecimal::ROUND_DOWN,      :truncate],
+    [ BigDecimal::ROUND_HALF_UP,   :half_up],
+    [ BigDecimal::ROUND_HALF_UP,   :default],
+    [ BigDecimal::ROUND_HALF_DOWN, :half_down],
+    [ BigDecimal::ROUND_HALF_EVEN, :half_even],
+    [ BigDecimal::ROUND_HALF_EVEN, :banker],
+    [ BigDecimal::ROUND_CEILING,   :ceiling],
+    [ BigDecimal::ROUND_CEILING,   :ceil],
+    [ BigDecimal::ROUND_FLOOR,     :floor],
+  ]
+
   def test_version
     assert_equal("1.0.1", BigDecimal.ver)
   end
@@ -60,6 +74,13 @@
     ensure
       BigDecimal.mode(BigDecimal::ROUND_MODE, saved_mode)
     end
+
+    BigDecimal.save_rounding_mode do
+      ROUNDING_MODE_MAP.each do |const, sym|
+        BigDecimal.mode(BigDecimal::ROUND_MODE, sym)
+        assert_equal(const, BigDecimal.mode(BigDecimal::ROUND_MODE))
+      end
+    end
   end
 
   def test_thread_local_mode
@@ -613,16 +634,21 @@
     assert_equal(2, x.round(0, BigDecimal::ROUND_FLOOR))
     assert_raise(TypeError) { x.round(0, 256) }
 
+    ROUNDING_MODE_MAP.each do |const, sym|
+      assert_equal(x.round(0, const), x.round(0, sym))
+    end
+
+    bug3803 = '[ruby-core:32136]'
     15.times do |n|
       x = BigDecimal.new("5#{'0'*n}1")
-      assert_equal(10**(n+2), x.round(-(n+2), BigDecimal::ROUND_HALF_DOWN))
-      assert_equal(10**(n+2), x.round(-(n+2), BigDecimal::ROUND_HALF_EVEN))
+      assert_equal(10**(n+2), x.round(-(n+2), BigDecimal::ROUND_HALF_DOWN), bug3803)
+      assert_equal(10**(n+2), x.round(-(n+2), BigDecimal::ROUND_HALF_EVEN), bug3803)
       x = BigDecimal.new("0.5#{'0'*n}1")
-      assert_equal(1, x.round(0, BigDecimal::ROUND_HALF_DOWN))
-      assert_equal(1, x.round(0, BigDecimal::ROUND_HALF_EVEN))
+      assert_equal(1, x.round(0, BigDecimal::ROUND_HALF_DOWN), bug3803)
+      assert_equal(1, x.round(0, BigDecimal::ROUND_HALF_EVEN), bug3803)
       x = BigDecimal.new("-0.5#{'0'*n}1")
-      assert_equal(-1, x.round(0, BigDecimal::ROUND_HALF_DOWN))
-      assert_equal(-1, x.round(0, BigDecimal::ROUND_HALF_EVEN))
+      assert_equal(-1, x.round(0, BigDecimal::ROUND_HALF_DOWN), bug3803)
+      assert_equal(-1, x.round(0, BigDecimal::ROUND_HALF_EVEN), bug3803)
     end
   end
 

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]