Skip to content

Commit 5604c75

Browse files
committed
Implement hyperbolic functions(sinh, cosh, tanh)
1 parent e513924 commit 5604c75

File tree

2 files changed

+96
-0
lines changed

2 files changed

+96
-0
lines changed

lib/bigdecimal/math.rb

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414
# acos(x, prec)
1515
# atan(x, prec)
1616
# atan2(y, x, prec)
17+
# sinh (x, prec)
18+
# cosh (x, prec)
19+
# tanh (x, prec)
1720
# PI (prec)
1821
# E (prec) == exp(1.0,prec)
1922
#
@@ -339,6 +342,72 @@ def atan2(y, x, prec)
339342
neg ? -v : v
340343
end
341344

345+
# call-seq:
346+
# sinh(decimal, numeric) -> BigDecimal
347+
#
348+
# Computes the hyperbolic sine of +decimal+ to the specified number of digits of
349+
# precision, +numeric+.
350+
#
351+
# If +decimal+ is NaN, returns NaN.
352+
#
353+
# BigMath.sinh(BigDecimal('1'), 16).to_s
354+
# #=> "0.11752011936438014568823818505956e1"
355+
#
356+
def sinh(x, prec)
357+
raise ArgumentError, "Zero or negative precision for sinh" if prec <= 0
358+
return BigDecimal::NAN if x.nan?
359+
return x if x.infinite?
360+
361+
prec += BigDecimal.double_fig
362+
prec -= x.exponent if x.exponent < 0
363+
e = BigMath.exp(x, prec)
364+
(e - BigDecimal(1).div(e, prec)).div(2, prec)
365+
end
366+
367+
# call-seq:
368+
# cosh(decimal, numeric) -> BigDecimal
369+
#
370+
# Computes the hyperbolic cosine of +decimal+ to the specified number of digits of
371+
# precision, +numeric+.
372+
#
373+
# If +decimal+ is NaN, returns NaN.
374+
#
375+
# BigMath.cosh(BigDecimal('1'), 16).to_s
376+
# #=> "0.15430806348152437784779056207571e1"
377+
#
378+
def cosh(x, prec)
379+
raise ArgumentError, "Zero or negative precision for cosh" if prec <= 0
380+
return BigDecimal::NAN if x.nan?
381+
return BigDecimal::INFINITY if x.infinite?
382+
383+
prec += BigDecimal.double_fig
384+
e = BigMath.exp(x, prec)
385+
(e + BigDecimal(1).div(e, prec)).div(2, prec)
386+
end
387+
388+
# call-seq:
389+
# tanh(decimal, numeric) -> BigDecimal
390+
#
391+
# Computes the hyperbolic tangent of +decimal+ to the specified number of digits of
392+
# precision, +numeric+.
393+
#
394+
# If +decimal+ is NaN, returns NaN.
395+
#
396+
# BigMath.tanh(BigDecimal('1'), 16).to_s
397+
# #=> "0.7615941559557648881194582826048e0"
398+
#
399+
def tanh(x, prec)
400+
raise ArgumentError, "Zero or negative precision for tanh" if prec <= 0
401+
return BigDecimal::NAN if x.nan?
402+
return BigDecimal(x.infinite?) if x.infinite?
403+
404+
prec += BigDecimal.double_fig
405+
prec2 = prec + [-x.exponent, 0].max
406+
e = BigMath.exp(x, prec2)
407+
einv = BigDecimal(1).div(e, prec2)
408+
(e - einv).div(e + einv, prec)
409+
end
410+
342411
# call-seq:
343412
# PI(numeric) -> BigDecimal
344413
#

test/bigdecimal/test_bigmath.rb

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,33 @@ def test_atan2
199199
end
200200
end
201201

202+
def test_hyperbolic
203+
[-1, 0, 0.5, 1, 10].each do |x|
204+
assert_in_delta(Math.sinh(x), sinh(BigDecimal(x.to_s), N))
205+
assert_in_delta(Math.cosh(x), cosh(BigDecimal(x.to_s), N))
206+
assert_in_delta(Math.tanh(x), tanh(BigDecimal(x.to_s), N))
207+
end
208+
[MINF, BigDecimal(0), PINF].each do |x|
209+
assert_equal(Math.sinh(x.to_f), sinh(x, N).to_f)
210+
assert_equal(Math.cosh(x.to_f), cosh(x, N).to_f)
211+
assert_equal(Math.tanh(x.to_f), tanh(x, N).to_f)
212+
end
213+
214+
x = BigDecimal("0.3")
215+
assert_in_delta(tanh(x, 100), sinh(x, 100) / cosh(x, 100), BigDecimal("1e-100"))
216+
217+
e = E(116)
218+
assert_in_delta((e - 1 / e) / 2, sinh(BigDecimal(1), 100), BigDecimal("1e-100"))
219+
assert_in_delta((e + 1 / e) / 2, cosh(BigDecimal(1), 100), BigDecimal("1e-100"))
220+
assert_in_delta((e - 1 / e) / (e + 1 / e), tanh(BigDecimal(1), 100), BigDecimal("1e-100"))
221+
222+
["1e-30", "0.2", "10", "100"].each do |x|
223+
assert_relative_precision {|n| sinh(BigDecimal(x), n)}
224+
assert_relative_precision {|n| cosh(BigDecimal(x), n)}
225+
assert_relative_precision {|n| tanh(BigDecimal(x), n)}
226+
end
227+
end
228+
202229
def test_exp
203230
[-100, -2, 0.5, 10, 100].each do |x|
204231
assert_in_epsilon(Math.exp(x), BigMath.exp(BigDecimal(x, 0), N))

0 commit comments

Comments
 (0)