Skip to content

Commit ecf6996

Browse files
committed
Implement BigMath.log1p and BigMath.expm1
1 parent c9fbffc commit ecf6996

File tree

2 files changed

+58
-0
lines changed

2 files changed

+58
-0
lines changed

lib/bigdecimal/math.rb

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
# atanh(x, prec)
2323
# log2 (x, prec)
2424
# log10(x, prec)
25+
# log1p(x, prec)
26+
# expm1(x, prec)
2527
# PI (prec)
2628
# E (prec) == exp(1.0,prec)
2729
#
@@ -524,6 +526,42 @@ def log10(x, prec)
524526
v.mult(1, prec)
525527
end
526528

529+
# call-seq:
530+
# BigMath.log1p(decimal, numeric) -> BigDecimal
531+
#
532+
# Computes log(1 + decimal) to the specified number of digits of precision, +numeric+.
533+
def log1p(x, prec)
534+
BigDecimal::Internal.validate_prec(prec, :log1p)
535+
x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :log1p)
536+
raise Math::DomainError, 'Out of domain argument for log1p' if x < -1
537+
538+
return BigMath.log(x + 1, prec)
539+
end
540+
541+
# call-seq:
542+
# BigMath.expm1(decimal, numeric) -> BigDecimal
543+
#
544+
# Computes exp(decimal) - 1 to the specified number of digits of precision, +numeric+.
545+
#
546+
def expm1(x, prec)
547+
BigDecimal::Internal.validate_prec(prec, :expm1)
548+
x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :expm1)
549+
return BigDecimal(-1) if x.infinite? == -1
550+
551+
exp_prec = prec
552+
if x < -1
553+
# log10(exp(x)) = x * log10(e)
554+
lg_e = 0.4342944819032518
555+
exp_prec = prec + (lg_e * x).ceil + 2
556+
elsif x < 1
557+
exp_prec = prec - x.exponent + 2
558+
else
559+
exp_prec = prec
560+
end
561+
exp_prec > 0 ? BigMath.exp(x, exp_prec).sub(1, prec) : BigDecimal(-1)
562+
end
563+
564+
527565
# call-seq:
528566
# PI(numeric) -> BigDecimal
529567
#

test/bigdecimal/test_bigmath.rb

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,4 +363,24 @@ def test_log10
363363
end
364364
end
365365
end
366+
367+
def test_log1p
368+
assert_raise(Math::DomainError) { log1p(MINF, N) }
369+
assert_raise(Math::DomainError) { log1p(BigDecimal("-1.01"), N) }
370+
assert_in_epsilon(Math.log(0.01), log1p(BigDecimal("-0.99"), N))
371+
assert_positive_infinite_calculation { log1p(PINF, N) }
372+
assert_in_exact_precision(BigMath.log(1 + BigDecimal("1e-20"), 100), log1p(BigDecimal("1e-20"), 100), 100)
373+
end
374+
375+
def test_expm1
376+
assert_equal(-1, expm1(MINF, N))
377+
assert_positive_infinite_calculation { expm1(PINF, N) }
378+
assert_equal(-1, expm1(BigDecimal("-400"), 100))
379+
assert_equal(-1, expm1(BigDecimal("-231"), 100))
380+
assert_not_equal(-1, expm1(BigDecimal("-229"), 100))
381+
assert_in_exact_precision(BigMath.exp(-220, 100) - 1, expm1(BigDecimal("-220"), 100), 100)
382+
assert_in_exact_precision(BigMath.exp(-3, 100) - 1, expm1(BigDecimal("-3"), 100), 100)
383+
assert_in_exact_precision(BigMath.exp(BigDecimal("1.23e-10"), 120) - 1, expm1(BigDecimal("1.23e-10"), 100), 100)
384+
assert_in_exact_precision(BigMath.exp(123, 120) - 1, expm1(BigDecimal("123"), 100), 100)
385+
end
366386
end

0 commit comments

Comments
 (0)