* 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. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@32150 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
a489109884
commit
f107d1e706
12
ChangeLog
12
ChangeLog
@ -1,3 +1,15 @@
|
||||
Sat Jun 18 02:30:00 2011 Kenta Murata <mrkn@mrkn.jp>
|
||||
|
||||
* 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@dotrb.org>
|
||||
|
||||
* ext/date/date_core.c: do not define wnum[01].
|
||||
|
@ -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 @@ BigDecimal_save_limit(VALUE self)
|
||||
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 @@ Init_bigdecimal(void)
|
||||
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");
|
||||
|
@ -7,7 +7,6 @@ require 'bigdecimal'
|
||||
# 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 @@ module BigMath
|
||||
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.
|
||||
#
|
||||
|
@ -896,4 +896,56 @@ class TestBigDecimal < Test::Unit::TestCase
|
||||
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
|
||||
|
@ -61,22 +61,6 @@ class TestBigMath < Test::Unit::TestCase
|
||||
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))
|
||||
|
Loading…
x
Reference in New Issue
Block a user