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/