diff --git a/lib/bigdecimal.rb b/lib/bigdecimal.rb index 40d9cffc..f11c6217 100644 --- a/lib/bigdecimal.rb +++ b/lib/bigdecimal.rb @@ -19,18 +19,15 @@ class BigDecimal # Related: BigDecimal#power. # def **(y) - unless y.is_a?(BigDecimal) - case y - when Integer, Float, Rational - y = BigDecimal(y, 0) - when nil - raise TypeError, 'wrong argument type NilClass' - else - x, y = y.coerce(self) - return x**y - end + case y + when BigDecimal, Integer, Float, Rational + power(y) + when nil + raise TypeError, 'wrong argument type NilClass' + else + x, y = y.coerce(self) + x**y end - power(y) end # call-seq: @@ -44,7 +41,7 @@ def **(y) def power(y, prec = nil) BigMath._validate_prec(prec, :power) if prec x = self - y = BigMath._coerce_to_bigdecimal(y, :power) + y = BigMath._coerce_to_bigdecimal(y, prec || n_significant_digits, :power) return BigMath._nan_computation_result if x.nan? || y.nan? return BigDecimal(1) if y.zero? @@ -145,12 +142,17 @@ def power(y, prec = nil) # Core BigMath methods for BigDecimal (log, exp) are defined here. # Other methods (sin, cos, atan) are defined in 'bigdecimal/math.rb'. module BigMath - def self._coerce_to_bigdecimal(x, method_name, complex_domain_error = false) # :nodoc: + + # Coerce x to BigDecimal with the specified precision. + # TODO: some methods (example: BigMath.exp) require more precision than specified to coerce. + def self._coerce_to_bigdecimal(x, prec, method_name, complex_domain_error = false) # :nodoc: case x when BigDecimal return x - when Integer, Float, Rational - return BigDecimal(x, 0) + when Integer, Float + return BigDecimal(x) + when Rational + return BigDecimal(x, [prec, 2 * BigDecimal.double_fig].max) when Complex if complex_domain_error raise Math::DomainError, "Complex argument for BigMath.#{method_name}" @@ -192,7 +194,7 @@ def self._nan_computation_result # :nodoc: # def self.log(x, prec) _validate_prec(prec, :log) - x = _coerce_to_bigdecimal(x, :log, true) + x = _coerce_to_bigdecimal(x, prec, :log, true) return _nan_computation_result if x.nan? raise Math::DomainError, 'Zero or negative argument for log' if x <= 0 return _infinity_computation_result if x.infinite? @@ -258,7 +260,7 @@ def self.log(x, prec) # def self.exp(x, prec) _validate_prec(prec, :exp) - x = _coerce_to_bigdecimal(x, :exp) + x = _coerce_to_bigdecimal(x, prec, :exp) return _nan_computation_result if x.nan? return x.positive? ? _infinity_computation_result : BigDecimal(0) if x.infinite? return BigDecimal(1) if x.zero? diff --git a/test/bigdecimal/test_bigdecimal.rb b/test/bigdecimal/test_bigdecimal.rb index d19a5bc3..46579514 100644 --- a/test/bigdecimal/test_bigdecimal.rb +++ b/test/bigdecimal/test_bigdecimal.rb @@ -1895,6 +1895,19 @@ def test_power_with_prec assert_equal(BigDecimal('0.5394221232e-7'), BigDecimal('0.12345').power(8, 10)) end + def test_power_with_rational + x1 = BigDecimal(2) + x2 = BigDecimal('1.' + '1' * 100) + y = 3 / 7r + z1 = x1.power(BigDecimal(y, 100), 100) + z2 = x2.power(BigDecimal(y, 100), 100) + assert_in_epsilon(z1, x1.power(y, 100), 1e-99) + assert_in_epsilon(z1, x1.power(y), 1e-30) + assert_in_epsilon(z1, x1 ** y, 1e-30) + assert_in_epsilon(z2, x2.power(y), 1e-99) + assert_in_epsilon(z2, x2 ** y, 1e-99) + end + def test_power_precision x = BigDecimal("1.41421356237309504880168872420969807856967187537695") y = BigDecimal("3.14159265358979323846264338327950288419716939937511") @@ -2153,6 +2166,7 @@ def test_BigMath_exp_with_rational assert_in_epsilon(Math.exp(40), BigMath.exp(Rational(80,2), prec)) assert_in_epsilon(Math.exp(-20), BigMath.exp(Rational(-40,2), prec)) assert_in_epsilon(Math.exp(-40), BigMath.exp(Rational(-80,2), prec)) + assert_in_epsilon(BigMath.exp(BigDecimal(3 / 7r, 100), 100), BigMath.exp(3 / 7r, 100), 1e-99) end def test_BigMath_exp_under_gc_stress @@ -2285,6 +2299,10 @@ def test_BigMath_log_with_reciprocal_of_42 assert_in_delta(Math.log(1e-42), BigMath.log(BigDecimal("1e-42"), 20)) end + def test_BigMath_log_with_rational + assert_in_epsilon(BigMath.log(BigDecimal(3 / 7r, 100), 100), BigMath.log(3 / 7r, 100), 1e-99) + end + def test_BigMath_log_under_gc_stress paths = $LOAD_PATH.map{|path| "-I#{path}" } assert_in_out_err([*paths, "-rbigdecimal", "--disable-gems"], <<-EOS, [], [])