* 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].


git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@25013 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
nobu 2009-09-20 15:07:38 +00:00
parent 970ec9c02d
commit cd4f59bc5e
4 changed files with 116 additions and 7 deletions

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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"