Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion lib/bigdecimal.rb
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,14 @@ def log(x, prec)
prec = BigDecimal::Internal.coerce_validate_prec(prec, :log)
raise Math::DomainError, 'Complex argument for BigMath.log' if Complex === x

x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :log)
if Rational === x && x.between?(1/2r, 2r)
# Preserve the significant digits of the difference from one.
input_prec = [prec + BigDecimal::Internal::EXTRA_PREC, 2 * BigDecimal.double_fig].max
difference = BigDecimal(x - 1, input_prec)
x = difference.add(1, input_prec + [1 - difference.exponent, 0].max)
else
x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :log)
end
return BigDecimal::Internal.nan_computation_result if x.nan?
raise Math::DomainError, 'Negative argument for log' if x < 0
return -BigDecimal::Internal.infinity_computation_result if x.zero?
Expand Down
44 changes: 44 additions & 0 deletions test/bigdecimal/test_bigdecimal.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2446,6 +2446,7 @@ def test_BigMath_log_with_1
assert_in_delta(0.0, BigMath.log(1, 20))
assert_in_delta(0.0, BigMath.log(1.0, 20))
assert_in_delta(0.0, BigMath.log(BigDecimal(1), 20))
assert_positive_zero(BigMath.log(1r, 20))
end

def test_BigMath_log_with_exp_1
Expand Down Expand Up @@ -2488,6 +2489,49 @@ 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_with_rational_close_to_one
n = 12345**9
# The first two and three terms of log(1 + 1/n)'s series round to this reference.
expected = BigDecimal("1.5016895257722442449503942510970658615421451954590e-37")
assert_in_delta(expected, BigMath.log(Rational(n + 1, n), 50), BigDecimal("1e-85"))
assert_in_delta(-expected, BigMath.log(Rational(n, n + 1), 50), BigDecimal("1e-85"))
end

def test_BigMath_log_with_rational_close_to_one_at_low_precision
n = 12345**9
assert_in_delta(BigDecimal("1.5017e-37"), BigMath.log(Rational(n + 1, n), 5), BigDecimal("1e-41"))
end

def test_BigMath_log_with_rational_closer_to_one_than_precision
n = 10**100
assert_in_delta(BigDecimal("1e-100"), BigMath.log(Rational(n + 1, n), 50), BigDecimal("1e-148"))
assert_in_delta(BigDecimal("-1e-100"), BigMath.log(Rational(n, n + 1), 50), BigDecimal("1e-148"))
end

def test_BigMath_log_with_rational_close_to_one_with_limit
n = 12345**9
expected = BigDecimal("1.5016895257722442449503942510970658615421451954590e-37")
BigDecimal.save_rounding_mode do
[:half_up, :down, :ceiling, :floor].each do |mode|
BigDecimal.mode(BigDecimal::ROUND_MODE, mode)
upper, lower = BigDecimal.save_limit do
BigDecimal.limit(10)
results = [BigMath.log(Rational(n + 1, n), 50), BigMath.log(Rational(n, n + 1), 50)]
assert_equal(10, BigDecimal.limit)
results
end
assert_in_delta(expected, upper, BigDecimal("1e-85"), "rounding mode: #{mode}")
assert_in_delta(-expected, lower, BigDecimal("1e-85"), "rounding mode: #{mode}")
end
end
end

def test_BigMath_log_with_exact_rationals
[1/4r, 1/2r, 3/2r, 2r, 3r].each do |x|
assert_equal(BigMath.log(BigDecimal(x, 50), 50), BigMath.log(x, 50))
end
end

def test_BigMath_log_under_gc_stress
paths = $LOAD_PATH.map{|path| "-I#{path}" }
assert_in_out_err([*paths, "-rbigdecimal", "--disable-gems"], <<-EOS, [], [])
Expand Down