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

ruby-changes:20104

From: mrkn <ko1@a...>
Date: Sat, 18 Jun 2011 02:38:26 +0900 (JST)
Subject: [ruby-changes:20104] mrkn:r32150 (trunk): * ext/bigdecimal/bigdecimal.c (BigMath_s_exp): move BigMath.exp from

mrkn	2011-06-18 02:38:14 +0900 (Sat, 18 Jun 2011)

  New Revision: 32150

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

  Log:
    * ext/bigdecimal/bigdecimal.c (BigMath_s_exp): move BigMath.exp from
      bigdecimal/math.rb.
    * ext/bigdecimal/lib/bigdecimal/math.rb: ditto.
    * test/bigdecimal/test_bigdecimal.rb: move test for BigMath.exp from
      test/bigdecimal/test_bigmath.rb.
    * test/bigdecimal/test_bigmath.rb: ditto.

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

Index: ChangeLog
===================================================================
--- ChangeLog	(revision 32149)
+++ ChangeLog	(revision 32150)
@@ -1,3 +1,15 @@
+Sat Jun 18 02:30:00 2011  Kenta Murata  <mrkn@m...>
+
+	* ext/bigdecimal/bigdecimal.c (BigMath_s_exp): move BigMath.exp from
+	  bigdecimal/math.rb.
+
+	* ext/bigdecimal/lib/bigdecimal/math.rb: ditto.
+
+	* test/bigdecimal/test_bigdecimal.rb: move test for BigMath.exp from
+	  test/bigdecimal/test_bigmath.rb.
+
+	* test/bigdecimal/test_bigmath.rb: ditto.
+
 Sat Jun 18 00:20:54 2011  Tadayoshi Funaba  <tadf@d...>
 
 	* ext/date/date_core.c: do not define wnum[01].
Index: ext/bigdecimal/bigdecimal.c
===================================================================
--- ext/bigdecimal/bigdecimal.c	(revision 32149)
+++ ext/bigdecimal/bigdecimal.c	(revision 32150)
@@ -34,6 +34,7 @@
 /* #define ENABLE_NUMERIC_STRING */
 
 VALUE rb_cBigDecimal;
+VALUE rb_mBigMath;
 
 static ID id_BigDecimal_exception_mode;
 static ID id_BigDecimal_rounding_mode;
@@ -2000,6 +2001,137 @@
     return ret;
 }
 
+/* call-seq:
+ * BigMath.exp(x, prec)
+ *
+ * Computes the value of e (the base of natural logarithms) raised to the
+ * power of x, to the specified number of digits of precision.
+ *
+ * If x is infinite, returns Infinity.
+ *
+ * If x is NaN, returns NaN.
+ */
+static VALUE
+BigMath_s_exp(VALUE klass, VALUE x, VALUE vprec)
+{
+    ssize_t prec, n, i;
+    Real* vx = NULL;
+    VALUE one, d, x1, y, z;
+    int negative = 0;
+    int infinite = 0;
+    int nan = 0;
+    double flo;
+
+    prec = NUM2SSIZET(vprec);
+    if (prec <= 0) {
+	rb_raise(rb_eArgError, "Zero or negative precision for exp");
+    }
+
+    /* TODO: the following switch statement is almostly the same as one in the
+     *       BigDecimalCmp function. */
+    switch (TYPE(x)) {
+      case T_DATA:
+	  if (!is_kind_of_BigDecimal(x)) break;
+	  vx = DATA_PTR(x);
+	  negative = VpGetSign(vx) < 0;
+	  infinite = VpIsPosInf(vx) || VpIsNegInf(vx);
+	  nan = VpIsNaN(vx);
+	  break;
+
+      case T_FIXNUM:
+	  /* fall through */
+      case T_BIGNUM:
+	  vx = GetVpValue(x, 0);
+	  break;
+
+      case T_FLOAT:
+	flo = RFLOAT_VALUE(x);
+	negative = flo < 0;
+	infinite = isinf(flo);
+	nan = isnan(flo);
+	if (!infinite && !nan) {
+	    vx = GetVpValueWithPrec(x, DBL_DIG+1, 0);
+	}
+	break;
+
+      case T_RATIONAL:
+	vx = GetVpValueWithPrec(x, prec, 0);
+	break;
+
+      default:
+	break;
+    }
+    if (infinite) {
+	if (negative) {
+	    return ToValue(GetVpValueWithPrec(INT2NUM(0), prec, 1));
+	}
+	else {
+	    Real* vy;
+	    vy = VpCreateRbObject(prec, "#0");
+	    RB_GC_GUARD(vy->obj);
+	    VpSetInf(vy, VP_SIGN_POSITIVE_INFINITE);
+	    return ToValue(vy);
+	}
+    }
+    else if (nan) {
+	Real* vy;
+	vy = VpCreateRbObject(prec, "#0");
+	RB_GC_GUARD(vy->obj);
+	VpSetNaN(vy);
+	return ToValue(vy);
+    }
+    else if (vx == NULL) {
+	rb_raise(rb_eArgError, "%s can't be coerced into BigDecimal",
+		 rb_special_const_p(x) ? RSTRING_PTR(rb_inspect(x)) : rb_obj_classname(x));
+    }
+    RB_GC_GUARD(vx->obj);
+
+    n = prec + rmpd_double_figures();
+    negative = VpGetSign(vx) < 0;
+    if (negative) {
+	VpSetSign(vx, 1);
+    }
+
+    RB_GC_GUARD(one) = ToValue(VpCreateRbObject(1, "1"));
+    RB_GC_GUARD(x1) = one;
+    RB_GC_GUARD(y)  = one;
+    RB_GC_GUARD(d)  = y;
+    RB_GC_GUARD(z)  = one;
+    i  = 0;
+
+    while (!VpIsZero((Real*)DATA_PTR(d))) {
+	VALUE argv[2];
+	SIGNED_VALUE const ey = VpExponent10(DATA_PTR(y));
+	SIGNED_VALUE const ed = VpExponent10(DATA_PTR(d));
+	ssize_t m = n - vabs(ey - ed);
+	if (m <= 0) {
+	    break;
+	}
+	else if ((size_t)m < rmpd_double_figures()) {
+	    m = rmpd_double_figures();
+	}
+
+	x1 = BigDecimal_mult2(x1, x, SSIZET2NUM(n));
+	++i;
+	z = BigDecimal_mult(z, SSIZET2NUM(i));
+	argv[0] = z;
+	argv[1] = SSIZET2NUM(m);
+	d = BigDecimal_div2(2, argv, x1);
+	y = BigDecimal_add(y, d);
+    }
+
+    if (negative) {
+	VALUE argv[2];
+	argv[0] = y;
+	argv[1] = vprec;
+	return BigDecimal_div2(2, argv, one);
+    }
+    else {
+	vprec = SSIZET2NUM(prec - VpExponent10(DATA_PTR(y)));
+	return BigDecimal_round(1, &vprec, y);
+    }
+}
+
 /* Document-class: BigDecimal
  * BigDecimal provides arbitrary-precision floating point decimal arithmetic.
  *
@@ -2295,6 +2427,10 @@
     rb_define_method(rb_cBigDecimal, "truncate",  BigDecimal_truncate, -1);
     rb_define_method(rb_cBigDecimal, "_dump", BigDecimal_dump, -1);
 
+    /* mathematical functions */
+    rb_mBigMath = rb_define_module("BigMath");
+    rb_define_singleton_method(rb_mBigMath, "exp", BigMath_s_exp, 2);
+
     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");
Index: ext/bigdecimal/lib/bigdecimal/math.rb
===================================================================
--- ext/bigdecimal/lib/bigdecimal/math.rb	(revision 32149)
+++ ext/bigdecimal/lib/bigdecimal/math.rb	(revision 32150)
@@ -7,7 +7,6 @@
 #   sin (x, prec)
 #   cos (x, prec)
 #   atan(x, prec)  Note: |x|<1, x=0.9999 may not converge.
-#   exp (x, prec)
 #   log (x, prec)
 #   PI  (prec)
 #   E   (prec) == exp(1.0,prec)
@@ -146,47 +145,6 @@
     y
   end
 
-  # Computes the value of e (the base of natural logarithms) raised to the
-  # power of x, to the specified number of digits of precision.
-  #
-  # If x is infinite or NaN, returns NaN.
-  #
-  # BigMath::exp(BigDecimal.new('1'), 10).to_s
-  # -> "0.271828182845904523536028752390026306410273E1"
-  def exp(x, prec)
-    raise ArgumentError, "Zero or negative precision for exp" if prec <= 0
-    if x.infinite?
-      if x < 0
-        return BigDecimal("0", prec)
-      else
-        return BigDecimal("+Infinity", prec)
-      end
-    elsif x.nan?
-      return BigDecimal("NaN", prec)
-    end
-    n    = prec + BigDecimal.double_fig
-    one  = BigDecimal("1")
-    x = -x if neg = x < 0
-    x1 = one
-    y  = one
-    d  = y
-    z  = one
-    i  = 0
-    while d.nonzero? && ((m = n - (y.exponent - d.exponent).abs) > 0)
-      m = BigDecimal.double_fig if m < BigDecimal.double_fig
-      x1  = x1.mult(x,n)
-      i += 1
-      z *= i
-      d  = x1.div(z,m)
-      y += d
-    end
-    if neg
-      one.div(y, prec)
-    else
-      y.round(prec - y.exponent)
-    end
-  end
-
   # Computes the natural logarithm of x to the specified number of digits
   # of precision.
   #
Index: test/bigdecimal/test_bigdecimal.rb
===================================================================
--- test/bigdecimal/test_bigdecimal.rb	(revision 32149)
+++ test/bigdecimal/test_bigdecimal.rb	(revision 32150)
@@ -896,4 +896,56 @@
   def test_NAN
     assert(BigDecimal::NAN.nan?, "BigDecimal::NAN is not NaN")
   end
+
+  def test_exp_with_zerp_precision
+    assert_raise(ArgumentError) do
+      BigMath.exp(1, 0)
+    end
+  end
+
+  def test_exp_with_negative_precision
+    assert_raise(ArgumentError) do
+      BigMath.exp(1, -42)
+    end
+  end
+
+  def test_exp_with_complex
+    assert_raise(ArgumentError) do
+      BigMath.exp(Complex(1, 2), 20)
+    end
+  end
+
+  def test_exp_with_negative_infinite
+    BigDecimal.save_exception_mode do
+      BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false)
+      assert_equal(0, BigMath.exp(-BigDecimal::INFINITY, 20))
+    end
+  end
+
+  def test_exp_with_positive_infinite
+    BigDecimal.save_exception_mode do
+      BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false)
+      assert(BigMath.exp(BigDecimal::INFINITY, 20) > 0)
+      assert(BigMath.exp(BigDecimal::INFINITY, 20).infinite?)
+    end
+  end
+
+  def test_exp_with_nan
+    BigDecimal.save_exception_mode do
+      BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
+      assert(BigMath.exp(BigDecimal::NAN, 20).nan?)
+    end
+  end
+
+  def test_exp_with_1
+    assert_in_epsilon(Math::E, BigMath.exp(1, 20))
+  end
+
+  def test_BigMath_exp
+    n = 20
+    assert_in_epsilon(Math.exp(n), BigMath.exp(BigDecimal("20"), n))
+    assert_in_epsilon(Math.exp(40), BigMath.exp(BigDecimal("40"), n))
+    assert_in_epsilon(Math.exp(-n), BigMath.exp(BigDecimal("-20"), n))
+    assert_in_epsilon(Math.exp(-40), BigMath.exp(BigDecimal("-40"), n))
+  end
 end
Index: test/bigdecimal/test_bigmath.rb
===================================================================
--- test/bigdecimal/test_bigmath.rb	(revision 32149)
+++ test/bigdecimal/test_bigmath.rb	(revision 32150)
@@ -61,22 +61,6 @@
                  atan(BigDecimal("1.08"), 72).round(72), '[ruby-dev:41257]')
   end
 
-  def test_exp
-    assert_in_epsilon(Math::E, exp(BigDecimal("1"), N))
-    assert_in_epsilon(Math.exp(N), exp(BigDecimal("20"), N))
-    assert_in_epsilon(Math.exp(40), exp(BigDecimal("40"), N))
-    assert_in_epsilon(Math.exp(-N), exp(BigDecimal("-20"), N))
-    assert_in_epsilon(Math.exp(-40), exp(BigDecimal("-40"), N))
-    begin
-      old_mode = BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY)
-      BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false)
-      assert(exp(BigDecimal::INFINITY, N).infinite?, "exp(INFINITY) is not an infinity")
-    ensure
-      #BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, old_mode)
-    end
-    assert_equal(0.0, exp(-BigDecimal::INFINITY, N))
-  end
-
   def test_log
     assert_in_delta(0.0, log(BigDecimal("1"), N))
     assert_in_delta(1.0, log(E(N), N))

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

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