Skip to content

Commit 748996a

Browse files
committed
Implement BigMath.log1p and BigMath.expm1
1 parent 97b9002 commit 748996a

File tree

2 files changed

+65
-0
lines changed

2 files changed

+65
-0
lines changed

lib/bigdecimal/math.rb

Lines changed: 45 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,49 @@ 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+
#
534+
# BigMath.log1p(BigDecimal('0.1'), 32).to_s
535+
# #=> "0.95310179804324860043952123280765e-1"
536+
#
537+
def log1p(x, prec)
538+
BigDecimal::Internal.validate_prec(prec, :log1p)
539+
x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :log1p)
540+
raise Math::DomainError, 'Out of domain argument for log1p' if x < -1
541+
542+
return BigMath.log(x + 1, prec)
543+
end
544+
545+
# call-seq:
546+
# BigMath.expm1(decimal, numeric) -> BigDecimal
547+
#
548+
# Computes exp(decimal) - 1 to the specified number of digits of precision, +numeric+.
549+
#
550+
# BigMath.expm1(BigDecimal('0.1'), 32).to_s
551+
# #=> "0.10517091807564762481170782649025e0"
552+
#
553+
def expm1(x, prec)
554+
BigDecimal::Internal.validate_prec(prec, :expm1)
555+
x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :expm1)
556+
return BigDecimal(-1) if x.infinite? == -1
557+
558+
exp_prec = prec
559+
if x < -1
560+
# log10(exp(x)) = x * log10(e)
561+
lg_e = 0.4342944819032518
562+
exp_prec = prec + (lg_e * x).ceil + 2
563+
elsif x < 1
564+
exp_prec = prec - x.exponent + 2
565+
else
566+
exp_prec = prec
567+
end
568+
exp_prec > 0 ? BigMath.exp(x, exp_prec).sub(1, prec) : BigDecimal(-1)
569+
end
570+
571+
527572
# call-seq:
528573
# PI(numeric) -> BigDecimal
529574
#

test/bigdecimal/test_bigmath.rb

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

0 commit comments

Comments
 (0)