Skip to content

Commit ad7a103

Browse files
committed
Implement hyperbolic functions(sinh, cosh, tanh)
1 parent c4a14ac commit ad7a103

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
#
@@ -366,6 +369,72 @@ def atan2(y, x, prec)
366369
neg ? -v : v
367370
end
368371

372+
# call-seq:
373+
# sinh(decimal, numeric) -> BigDecimal
374+
#
375+
# Computes the hyperbolic sine of +decimal+ to the specified number of digits of
376+
# precision, +numeric+.
377+
#
378+
# If +decimal+ is NaN, returns NaN.
379+
#
380+
# BigMath.sinh(BigDecimal('1'), 16).to_s
381+
# #=> "0.11752011936438014568823818505956e1"
382+
#
383+
def sinh(x, prec)
384+
raise ArgumentError, "Zero or negative precision for sinh" if prec <= 0
385+
return BigDecimal::NAN if x.nan?
386+
return x if x.infinite?
387+
388+
prec += BigDecimal.double_fig
389+
prec -= x.exponent if x.exponent < 0
390+
e = BigMath.exp(x, prec)
391+
(e - BigDecimal(1).div(e, prec)).div(2, prec)
392+
end
393+
394+
# call-seq:
395+
# cosh(decimal, numeric) -> BigDecimal
396+
#
397+
# Computes the hyperbolic cosine of +decimal+ to the specified number of digits of
398+
# precision, +numeric+.
399+
#
400+
# If +decimal+ is NaN, returns NaN.
401+
#
402+
# BigMath.cosh(BigDecimal('1'), 16).to_s
403+
# #=> "0.15430806348152437784779056207571e1"
404+
#
405+
def cosh(x, prec)
406+
raise ArgumentError, "Zero or negative precision for cosh" if prec <= 0
407+
return BigDecimal::NAN if x.nan?
408+
return BigDecimal::INFINITY if x.infinite?
409+
410+
prec += BigDecimal.double_fig
411+
e = BigMath.exp(x, prec)
412+
(e + BigDecimal(1).div(e, prec)).div(2, prec)
413+
end
414+
415+
# call-seq:
416+
# tanh(decimal, numeric) -> BigDecimal
417+
#
418+
# Computes the hyperbolic tangent of +decimal+ to the specified number of digits of
419+
# precision, +numeric+.
420+
#
421+
# If +decimal+ is NaN, returns NaN.
422+
#
423+
# BigMath.tanh(BigDecimal('1'), 16).to_s
424+
# #=> "0.7615941559557648881194582826048e0"
425+
#
426+
def tanh(x, prec)
427+
raise ArgumentError, "Zero or negative precision for tanh" if prec <= 0
428+
return BigDecimal::NAN if x.nan?
429+
return BigDecimal(x.infinite?) if x.infinite?
430+
431+
prec += BigDecimal.double_fig
432+
prec2 = prec + [-x.exponent, 0].max
433+
e = BigMath.exp(x, prec2)
434+
einv = BigDecimal(1).div(e, prec2)
435+
(e - einv).div(e + einv, prec)
436+
end
437+
369438
# call-seq:
370439
# PI(numeric) -> BigDecimal
371440
#

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_log
203230
assert_equal(0, BigMath.log(BigDecimal("1.0"), 10))
204231
assert_in_epsilon(Math.log(10)*1000, BigMath.log(BigDecimal("1e1000"), 10))

0 commit comments

Comments
 (0)