Skip to content

Commit 80b40c9

Browse files
committed
Implement asin and acos
1 parent d3a1227 commit 80b40c9

File tree

2 files changed

+85
-1
lines changed

2 files changed

+85
-1
lines changed

lib/bigdecimal/math.rb

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
# sin (x, prec)
1111
# cos (x, prec)
1212
# tan (x, prec)
13+
# asin(x, prec)
14+
# acos(x, prec)
1315
# atan(x, prec)
1416
# atan2(y, x, prec)
1517
# PI (prec)
@@ -235,6 +237,55 @@ def tan(x, prec)
235237
sin(x, prec).div(cos, prec + BigDecimal.double_fig)
236238
end
237239

240+
# call-seq:
241+
# asin(decimal, numeric) -> BigDecimal
242+
#
243+
# Computes the arcsine of +decimal+ to the specified number of digits of
244+
# precision, +numeric+.
245+
#
246+
# If +decimal+ is NaN, returns NaN.
247+
#
248+
# BigMath.asin(BigDecimal('0.5'), 16).to_s
249+
# #=> "0.52359877559829887307710723054659e0"
250+
#
251+
def asin(x, prec)
252+
raise ArgumentError, "Zero or negative precision for asin" if prec <= 0
253+
raise Math::DomainError, "Out of domain argument for asin" if x < -1 || x > 1
254+
return BigDecimal::NAN if x.nan?
255+
prec2 = prec + BigDecimal.double_fig
256+
cos = (1 - x**2).sqrt(prec2)
257+
if cos.zero?
258+
pi = PI(prec)
259+
x > 0 ? pi / 2 : -pi / 2
260+
else
261+
atan(x.div(cos, prec2), prec)
262+
end
263+
end
264+
265+
# call-seq:
266+
# acos(decimal, numeric) -> BigDecimal
267+
#
268+
# Computes the arccosine of +decimal+ to the specified number of digits of
269+
# precision, +numeric+.
270+
#
271+
# If +decimal+ is NaN, returns NaN.
272+
#
273+
# BigMath.acos(BigDecimal('0.5'), 16).to_s
274+
# #=> "0.10471975511965977461542144610932e1"
275+
#
276+
def acos(x, prec)
277+
raise ArgumentError, "Zero or negative precision for acos" if prec <= 0
278+
raise Math::DomainError, "Out of domain argument for acos" if x < -1 || x > 1
279+
280+
return PI(prec) / 2 - asin(x, prec) if x < 0
281+
return PI(prec) / 2 if x.zero?
282+
return BigDecimal::NAN if x.nan?
283+
284+
prec2 = prec + BigDecimal.double_fig
285+
sin = (1 - x**2).sqrt(prec2)
286+
atan(sin.div(x, prec2), prec)
287+
end
288+
238289
# call-seq:
239290
# atan(decimal, numeric) -> BigDecimal
240291
#
@@ -271,7 +322,7 @@ def atan(x, prec)
271322
y *= 2 if dbl
272323
y = pi / 2 - y if inv
273324
y = -y if neg
274-
y
325+
y.mult(1, n)
275326
end
276327

277328
# call-seq:

test/bigdecimal/test_bigmath.rb

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,39 @@ def test_tan
132132
assert_relative_precision {|n| tan(PI(200) * 101 / 2, n) }
133133
end
134134

135+
def test_asin
136+
["-1", "-0.9", "-0.1", "0", "0.1", "0.9", "1"].each do |x|
137+
assert_in_delta(Math.asin(x.to_f), asin(BigDecimal(x), N))
138+
end
139+
assert_raise(Math::DomainError) { BigMath.asin(BigDecimal("1.1"), N) }
140+
assert_raise(Math::DomainError) { BigMath.asin(BigDecimal("-1.1"), N) }
141+
assert_in_delta(PI(100) / 6, asin(BigDecimal("0.5"), 100), BigDecimal("1e-100"))
142+
assert_relative_precision {|n| asin(BigDecimal("-0.4"), n) }
143+
assert_relative_precision {|n| asin(BigDecimal("0.3"), n) }
144+
assert_relative_precision {|n| asin(BigDecimal("0.9"), n) }
145+
assert_relative_precision {|n| asin(BigDecimal("0.#{"9" * 50}"), n) }
146+
assert_relative_precision {|n| asin(BigDecimal("0.#{"9" * 100}"), n) }
147+
assert_relative_precision {|n| asin(BigDecimal("0.#{"9" * 195}"), n) }
148+
assert_relative_precision {|n| asin(BigDecimal("1e-30"), n) }
149+
end
150+
151+
def test_acos
152+
["-1", "-0.9", "-0.1", "0", "0.1", "0.9", "1"].each do |x|
153+
assert_in_delta(Math.acos(x.to_f), acos(BigDecimal(x), N))
154+
end
155+
assert_raise(Math::DomainError) { BigMath.acos(BigDecimal("1.1"), N) }
156+
assert_raise(Math::DomainError) { BigMath.acos(BigDecimal("-1.1"), N) }
157+
assert_equal(0, acos(BigDecimal("1.0"), N))
158+
assert_in_delta(PI(100) / 3, acos(BigDecimal("0.5"), 100), BigDecimal("1e-100"))
159+
assert_relative_precision {|n| acos(BigDecimal("-0.4"), n) }
160+
assert_relative_precision {|n| acos(BigDecimal("0.3"), n) }
161+
assert_relative_precision {|n| acos(BigDecimal("0.9"), n) }
162+
assert_relative_precision {|n| acos(BigDecimal("0.#{"9" * 50}"), n) }
163+
assert_relative_precision {|n| acos(BigDecimal("0.#{"9" * 100}"), n) }
164+
assert_relative_precision {|n| acos(BigDecimal("0.#{"9" * 195}"), n) }
165+
assert_relative_precision {|n| acos(BigDecimal("1e-30"), n) }
166+
end
167+
135168
def test_atan
136169
assert_equal(0.0, atan(BigDecimal("0.0"), N))
137170
assert_in_delta(Math::PI/4, atan(BigDecimal("1.0"), N))

0 commit comments

Comments
 (0)