Skip to content

Commit 774d09b

Browse files
committed
Coerce rational with the given prec in exp, log and power calculation
1 parent ec2748a commit 774d09b

2 files changed

Lines changed: 37 additions & 17 deletions

File tree

lib/bigdecimal.rb

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,15 @@ class BigDecimal
1919
# Related: BigDecimal#power.
2020
#
2121
def **(y)
22-
unless y.is_a?(BigDecimal)
23-
case y
24-
when Integer, Float, Rational
25-
y = BigDecimal(y, 0)
26-
when nil
27-
raise TypeError, 'wrong argument type NilClass'
28-
else
29-
x, y = y.coerce(self)
30-
return x**y
31-
end
22+
case y
23+
when BigDecimal, Integer, Float, Rational
24+
power(y)
25+
when nil
26+
raise TypeError, 'wrong argument type NilClass'
27+
else
28+
x, y = y.coerce(self)
29+
x**y
3230
end
33-
power(y)
3431
end
3532

3633
# call-seq:
@@ -44,7 +41,7 @@ def **(y)
4441
def power(y, prec = nil)
4542
BigMath._validate_prec(prec, :power) if prec
4643
x = self
47-
y = BigMath._coerce_to_bigdecimal(y, :power)
44+
y = BigMath._coerce_to_bigdecimal(y, prec || n_significant_digits, :power)
4845

4946
return BigMath._nan_computation_result if x.nan? || y.nan?
5047
return BigDecimal(1) if y.zero?
@@ -145,12 +142,17 @@ def power(y, prec = nil)
145142
# Core BigMath methods for BigDecimal (log, exp) are defined here.
146143
# Other methods (sin, cos, atan) are defined in 'bigdecimal/math.rb'.
147144
module BigMath
148-
def self._coerce_to_bigdecimal(x, method_name, complex_domain_error = false) # :nodoc:
145+
146+
# Coerce x to BigDecimal with the specified precision.
147+
# TODO: some methods (example: BigMath.exp) require more precision than specified to coerce.
148+
def self._coerce_to_bigdecimal(x, prec, method_name, complex_domain_error = false) # :nodoc:
149149
case x
150150
when BigDecimal
151151
return x
152-
when Integer, Float, Rational
153-
return BigDecimal(x, 0)
152+
when Integer, Float
153+
return BigDecimal(x)
154+
when Rational
155+
return BigDecimal(x, [prec, 2 * BigDecimal.double_fig].max)
154156
when Complex
155157
if complex_domain_error
156158
raise Math::DomainError, "Complex argument for BigMath.#{method_name}"
@@ -192,7 +194,7 @@ def self._nan_computation_result # :nodoc:
192194
#
193195
def self.log(x, prec)
194196
_validate_prec(prec, :log)
195-
x = _coerce_to_bigdecimal(x, :log, true)
197+
x = _coerce_to_bigdecimal(x, prec, :log, true)
196198
return _nan_computation_result if x.nan?
197199
raise Math::DomainError, 'Zero or negative argument for log' if x <= 0
198200
return _infinity_computation_result if x.infinite?
@@ -258,7 +260,7 @@ def self.log(x, prec)
258260
#
259261
def self.exp(x, prec)
260262
_validate_prec(prec, :exp)
261-
x = _coerce_to_bigdecimal(x, :exp)
263+
x = _coerce_to_bigdecimal(x, prec, :exp)
262264
return _nan_computation_result if x.nan?
263265
return x.positive? ? _infinity_computation_result : BigDecimal(0) if x.infinite?
264266
return BigDecimal(1) if x.zero?

test/bigdecimal/test_bigdecimal.rb

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1895,6 +1895,19 @@ def test_power_with_prec
18951895
assert_equal(BigDecimal('0.5394221232e-7'), BigDecimal('0.12345').power(8, 10))
18961896
end
18971897

1898+
def test_power_with_rational
1899+
x1 = BigDecimal(2)
1900+
x2 = BigDecimal('1.' + '1' * 100)
1901+
y = 3 / 7r
1902+
z1 = x1.power(BigDecimal(y, 100), 100)
1903+
z2 = x2.power(BigDecimal(y, 100), 100)
1904+
assert_in_epsilon(z1, x1.power(y, 100), 1e-99)
1905+
assert_in_epsilon(z1, x1.power(y), 1e-30)
1906+
assert_in_epsilon(z1, x1 ** y, 1e-30)
1907+
assert_in_epsilon(z2, x2.power(y), 1e-99)
1908+
assert_in_epsilon(z2, x2 ** y, 1e-99)
1909+
end
1910+
18981911
def test_power_precision
18991912
x = BigDecimal("1.41421356237309504880168872420969807856967187537695")
19001913
y = BigDecimal("3.14159265358979323846264338327950288419716939937511")
@@ -2153,6 +2166,7 @@ def test_BigMath_exp_with_rational
21532166
assert_in_epsilon(Math.exp(40), BigMath.exp(Rational(80,2), prec))
21542167
assert_in_epsilon(Math.exp(-20), BigMath.exp(Rational(-40,2), prec))
21552168
assert_in_epsilon(Math.exp(-40), BigMath.exp(Rational(-80,2), prec))
2169+
assert_in_epsilon(BigMath.exp(BigDecimal(3 / 7r, 100), 100), BigMath.exp(3 / 7r, 100), 1e-99)
21562170
end
21572171

21582172
def test_BigMath_exp_under_gc_stress
@@ -2285,6 +2299,10 @@ def test_BigMath_log_with_reciprocal_of_42
22852299
assert_in_delta(Math.log(1e-42), BigMath.log(BigDecimal("1e-42"), 20))
22862300
end
22872301

2302+
def test_BigMath_log_with_rational
2303+
assert_in_epsilon(BigMath.log(BigDecimal(3 / 7r, 100), 100), BigMath.log(3 / 7r, 100), 1e-99)
2304+
end
2305+
22882306
def test_BigMath_log_under_gc_stress
22892307
paths = $LOAD_PATH.map{|path| "-I#{path}" }
22902308
assert_in_out_err([*paths, "-rbigdecimal", "--disable-gems"], <<-EOS, [], [])

0 commit comments

Comments
 (0)