Skip to content

Commit f2ccfc4

Browse files
committed
Implement hyperbolic functions(sinh, cosh, tanh)
1 parent ece0a08 commit f2ccfc4

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
#
@@ -355,6 +358,72 @@ def atan2(y, x, prec)
355358
neg ? -v : v
356359
end
357360

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

test/bigdecimal/test_bigmath.rb

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,33 @@ def test_atan2
197197
end
198198
end
199199

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

0 commit comments

Comments
 (0)