Skip to content

Commit e00dd39

Browse files
committed
Implement inverse hyperbolic functions(asinh, acosh, atanh)
1 parent f2ccfc4 commit e00dd39

File tree

2 files changed

+111
-0
lines changed

2 files changed

+111
-0
lines changed

lib/bigdecimal/math.rb

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
# sinh (x, prec)
1818
# cosh (x, prec)
1919
# tanh (x, prec)
20+
# asinh(x, prec)
21+
# acosh(x, prec)
22+
# atanh(x, prec)
2023
# PI (prec)
2124
# E (prec) == exp(1.0,prec)
2225
#
@@ -424,6 +427,68 @@ def tanh(x, prec)
424427
(e - einv).div(e + einv, prec)
425428
end
426429

430+
# call-seq:
431+
# asinh(decimal, numeric) -> BigDecimal
432+
#
433+
# Computes the inverse hyperbolic sine of +decimal+ to the specified number of digits of
434+
# precision, +numeric+.
435+
#
436+
# If +decimal+ is NaN, returns NaN.
437+
#
438+
# BigMath.asinh(BigDecimal('1'), 16).to_s
439+
# #=> "0.881373587019543025232609324892919887466177636058e0"
440+
#
441+
def asinh(x, prec)
442+
raise ArgumentError, "Zero or negative precision for tanh" if prec <= 0
443+
return x if x.nan? || x.infinite?
444+
return -asinh(-x, prec) if x < 0
445+
446+
sqrt_prec = prec + [-x.exponent, 0].max
447+
BigMath.log(x + sqrt(x**2 + 1, sqrt_prec), prec)
448+
end
449+
450+
# call-seq:
451+
# acosh(decimal, numeric) -> BigDecimal
452+
#
453+
# Computes the inverse hyperbolic cosine of +decimal+ to the specified number of digits of
454+
# precision, +numeric+.
455+
#
456+
# If +decimal+ is NaN, returns NaN.
457+
#
458+
# BigMath.acosh(BigDecimal('2'), 16).to_s
459+
# #=> "0.1316957896924816708625046347239934461496535769096e1"
460+
#
461+
def acosh(x, prec)
462+
raise ArgumentError, "Zero or negative precision for tanh" if prec <= 0
463+
raise Math::DomainError, "Out of domain argument for acosh" if x < 1
464+
return BigDecimal::INFINITY if x.infinite?
465+
return BigDecimal::NAN if x.nan?
466+
467+
BigMath.log(x + sqrt(x**2 - 1, prec), prec)
468+
end
469+
470+
# call-seq:
471+
# atanh(decimal, numeric) -> BigDecimal
472+
#
473+
# Computes the inverse hyperbolic tangent of +decimal+ to the specified number of digits of
474+
# precision, +numeric+.
475+
#
476+
# If +decimal+ is NaN, returns NaN.
477+
#
478+
# BigMath.atanh(BigDecimal('0.5'), 16).to_s
479+
# #=> "0.549306144334054845697622618461262852323745278910789847469990412e0"
480+
#
481+
def atanh(x, prec)
482+
raise ArgumentError, "Zero or negative precision for tanh" if prec <= 0
483+
raise Math::DomainError, "Out of domain argument for atanh" if x < -1 || x > 1
484+
return BigDecimal::NAN if x.nan?
485+
return BigDecimal::INFINITY if x == 1
486+
return -BigDecimal::INFINITY if x == -1
487+
488+
prec += BigDecimal.double_fig
489+
(BigMath.log(x + 1, prec) - BigMath.log(1 - x, prec)).div(2, prec)
490+
end
491+
427492
# call-seq:
428493
# PI(numeric) -> BigDecimal
429494
#

test/bigdecimal/test_bigmath.rb

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,52 @@ def test_hyperbolic
224224
end
225225
end
226226

227+
def test_asinh
228+
[-3, 0.5, 10].each do |x|
229+
assert_in_delta(Math.asinh(x), asinh(BigDecimal(x.to_s), N))
230+
end
231+
assert_equal(0, asinh(BigDecimal(0), N))
232+
assert_equal(PINF, asinh(PINF, N))
233+
assert_equal(MINF, asinh(MINF, N))
234+
235+
x = BigDecimal(1) / 7
236+
assert_in_delta(x, sinh(asinh(x, 100), 100), BigDecimal("1e-100"))
237+
238+
["1e-30", "0.2", "10", "100"].each do |x|
239+
assert_relative_precision {|n| asinh(BigDecimal(x), n)}
240+
end
241+
end
242+
243+
def test_acosh
244+
[1.5, 2, 10].each do |x|
245+
assert_in_delta(Math.acosh(x), acosh(BigDecimal(x.to_s), N))
246+
end
247+
assert_equal(0, acosh(BigDecimal(1), N))
248+
assert_equal(PINF, acosh(PINF, N))
249+
250+
x = BigDecimal(8) / 7
251+
assert_in_delta(x, cosh(acosh(x, 100), 100), BigDecimal("1e-100"))
252+
253+
["1." + "0" * 30 + "1", "1.5", "2", "100"].each do |x|
254+
assert_relative_precision {|n| acosh(BigDecimal(x), n)}
255+
end
256+
end
257+
258+
def test_atanh
259+
[-0.5, 0.1, 0.9].each do |x|
260+
assert_in_delta(Math.atanh(x), atanh(BigDecimal(x.to_s), N))
261+
end
262+
assert_equal(0, atanh(BigDecimal(0), N))
263+
assert_equal(PINF, atanh(BigDecimal(1), N))
264+
265+
x = BigDecimal(1) / 7
266+
assert_in_delta(x, tanh(atanh(x, 100), 100), BigDecimal("1e-100"))
267+
268+
["1e-30", "0.5", "0.9" + "9" * 30].each do |x|
269+
assert_relative_precision {|n| atanh(BigDecimal(x), n)}
270+
end
271+
end
272+
227273
def test_log
228274
assert_equal(0, BigMath.log(BigDecimal("1.0"), 10))
229275
assert_in_epsilon(Math.log(10)*1000, BigMath.log(BigDecimal("1e1000"), 10))

0 commit comments

Comments
 (0)