summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog6
-rw-r--r--ext/bigdecimal/lib/bigdecimal/math.rb36
-rw-r--r--test/bigdecimal/test_bigmath.rb77
-rw-r--r--version.h4
4 files changed, 116 insertions, 7 deletions
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 <nobu@ruby-lang.org>
+
+ * 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 <ruby-core@marc-andre.ca>
* 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"