diff --git a/ChangeLog b/ChangeLog index fcf26c0779..db8fed2c06 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +Mon Sep 21 00:07:36 2009 Nobuyoshi Nakada + + * ext/bigdecimal/lib/bigdecimal/math.rb (sin, cos, atan, exp, log): + improved precision and performance. based on a patch from Makoto + Yamashita in [ruby-core:25600] and [ruby-core:25602]. + Sun Sep 20 11:11:34 2009 Marc-Andre Lafortune * struct.c (rb_struct_equal, rb_struct_eql): Handle comparison of diff --git a/ext/bigdecimal/lib/bigdecimal/math.rb b/ext/bigdecimal/lib/bigdecimal/math.rb index 24e928b901..635992ef14 100644 --- a/ext/bigdecimal/lib/bigdecimal/math.rb +++ b/ext/bigdecimal/lib/bigdecimal/math.rb @@ -49,6 +49,14 @@ module BigMath n = prec + BigDecimal.double_fig one = BigDecimal("1") two = BigDecimal("2") + x = -x if neg = x < 0 + if x > (twopi = two * BigMath.PI(prec)) + if x > 30 + x %= twopi + else + x -= twopi while x > twopi + end + end x1 = x x2 = x.mult(x,n) sign = 1 @@ -65,7 +73,7 @@ module BigMath d = sign * x1.div(z,m) y += d end - y + neg ? -y : y end # Computes the cosine of x to the specified number of digits of precision. @@ -77,6 +85,14 @@ module BigMath n = prec + BigDecimal.double_fig one = BigDecimal("1") two = BigDecimal("2") + x = -x if x < 0 + if x > (twopi = two * BigMath.PI(prec)) + if x > 30 + x %= twopi + else + x -= twopi while x > twopi + end + end x1 = one x2 = x.mult(x,n) sign = 1 @@ -99,11 +115,9 @@ module BigMath # Computes the arctangent of x to the specified number of digits of precision. # # If x is infinite or NaN, returns NaN. - # Raises an argument error if x > 1. def atan(x, prec) raise ArgumentError, "Zero or negative precision for atan" if prec <= 0 return BigDecimal("NaN") if x.infinite? || x.nan? - raise ArgumentError, "x.abs must be less than 1.0" if x.abs>=1 n = prec + BigDecimal.double_fig y = x d = y @@ -132,6 +146,7 @@ module BigMath return BigDecimal("NaN") if x.infinite? || x.nan? n = prec + BigDecimal.double_fig one = BigDecimal("1") + x = -x if neg = x < 0 x1 = one y = one d = y @@ -145,7 +160,11 @@ module BigMath d = x1.div(z,m) y += d end - y + 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 @@ -159,6 +178,9 @@ module BigMath one = BigDecimal("1") two = BigDecimal("2") n = prec + BigDecimal.double_fig + if (expo = x.exponent) < 0 + x = x.mult(BigDecimal("1E#{-expo}"), n) + end x = (x - one).div(x + one,n) x2 = x.mult(x,n) y = x @@ -171,7 +193,11 @@ module BigMath d = x.div(i,m) y += d end - y*two + y *= two + if expo < 0 + y += log(BigDecimal("10"),prec) * BigDecimal(expo.to_s) + end + y end # Computes the value of pi to the specified number of digits of precision. diff --git a/test/bigdecimal/test_bigmath.rb b/test/bigdecimal/test_bigmath.rb new file mode 100644 index 0000000000..7801e6045b --- /dev/null +++ b/test/bigdecimal/test_bigmath.rb @@ -0,0 +1,77 @@ +require "test/unit" +require "bigdecimal" +require "bigdecimal/math" + +class TestBigMath < Test::Unit::TestCase + include BigMath + N = 20 + PINF = BigDecimal("+Infinity") + MINF = BigDecimal("-Infinity") + NAN = BigDecimal("NaN") + + def test_const + assert_in_delta(Math::PI, PI(N)) + assert_in_delta(Math::E, E(N)) + end + + def test_sqrt + assert_in_delta(2**0.5, sqrt(BigDecimal("2"), N)) + assert_equal(10, sqrt(BigDecimal("100"), N)) + assert_equal(0.0, sqrt(BigDecimal("0"), N)) + assert_equal(0.0, sqrt(BigDecimal("-0"), N)) + assert_raise(FloatDomainError) {sqrt(BigDecimal("-1.0"), N)} + assert_raise(FloatDomainError) {sqrt(NAN, N)} + assert_equal(PINF, sqrt(PINF, N)) + end + + def test_sin + assert_in_delta(0.0, sin(BigDecimal("0.0"), N)) + assert_in_delta(Math.sqrt(2.0) / 2, sin(PI(N) / 4, N)) + assert_in_delta(1.0, sin(PI(N) / 2, N)) + assert_in_delta(0.0, sin(PI(N) * 2, N)) + assert_in_delta(0.0, sin(PI(N), N)) + assert_in_delta(-1.0, sin(PI(N) / -2, N)) + assert_in_delta(0.0, sin(PI(N) * -2, N)) + assert_in_delta(0.0, sin(-PI(N), N)) + assert_in_delta(0.0, sin(PI(N) * 21, N)) + assert_in_delta(0.0, sin(PI(N) * 30, N)) + assert_in_delta(-1.0, sin(PI(N) * BigDecimal("301.5"), N)) + end + + def test_cos + assert_in_delta(1.0, cos(BigDecimal("0.0"), N)) + assert_in_delta(Math.sqrt(2.0) / 2, cos(PI(N) / 4, N)) + assert_in_delta(0.0, cos(PI(N) / 2, N)) + assert_in_delta(1.0, cos(PI(N) * 2, N)) + assert_in_delta(-1.0, cos(PI(N), N)) + assert_in_delta(0.0, cos(PI(N) / -2, N)) + assert_in_delta(1.0, cos(PI(N) * -2, N)) + assert_in_delta(-1.0, cos(-PI(N), N)) + assert_in_delta(-1.0, cos(PI(N) * 21, N)) + assert_in_delta(1.0, cos(PI(N) * 30, N)) + assert_in_delta(0.0, cos(PI(N) * BigDecimal("301.5"), N)) + end + + def test_atan + assert_equal(0.0, atan(BigDecimal("0.0"), N)) + assert_in_delta(Math::PI/4, atan(BigDecimal("1.0"), N)) + assert_in_delta(Math::PI/6, atan(sqrt(BigDecimal("3.0"), N) / 3, N)) + 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)) + end + + def test_log + assert_in_delta(0.0, log(BigDecimal("1"), N)) + assert_in_delta(1.0, log(E(N), N)) + assert_in_delta(Math.log(2.0), log(BigDecimal("2"), N)) + assert_in_delta(2.0, log(E(N)*E(N), N)) + assert_in_delta(Math.log(42.0), log(BigDecimal("42"), N)) + assert_in_delta(Math.log(1e-42), log(BigDecimal("1e-42"), N)) + end +end diff --git a/version.h b/version.h index 2ec9422ec7..c59750a8ca 100644 --- a/version.h +++ b/version.h @@ -1,5 +1,5 @@ #define RUBY_VERSION "1.9.2" -#define RUBY_RELEASE_DATE "2009-09-20" +#define RUBY_RELEASE_DATE "2009-09-21" #define RUBY_PATCHLEVEL -1 #define RUBY_BRANCH_NAME "trunk" @@ -8,7 +8,7 @@ #define RUBY_VERSION_TEENY 1 #define RUBY_RELEASE_YEAR 2009 #define RUBY_RELEASE_MONTH 9 -#define RUBY_RELEASE_DAY 20 +#define RUBY_RELEASE_DAY 21 #include "ruby/version.h"