Skip to content

Commit 04162f0

Browse files
committed
Implement hyperbolic functions(sinh, cosh, tanh)
1 parent fa5647e commit 04162f0

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
#
@@ -352,6 +355,72 @@ def atan2(y, x, prec)
352355
neg ? -v : v
353356
end
354357

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

test/bigdecimal/test_bigmath.rb

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,33 @@ def test_atan2
191191
end
192192
end
193193

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

0 commit comments

Comments
 (0)