Skip to content

Commit c4a14ac

Browse files
committed
Implement asin and acos
1 parent 67495c3 commit c4a14ac

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)
@@ -246,6 +248,55 @@ def tan(x, prec)
246248
sin.div(cos, prec + BigDecimal.double_fig)
247249
end
248250

251+
# call-seq:
252+
# asin(decimal, numeric) -> BigDecimal
253+
#
254+
# Computes the arcsine of +decimal+ to the specified number of digits of
255+
# precision, +numeric+.
256+
#
257+
# If +decimal+ is NaN, returns NaN.
258+
#
259+
# BigMath.asin(BigDecimal('0.5'), 16).to_s
260+
# #=> "0.52359877559829887307710723054659e0"
261+
#
262+
def asin(x, prec)
263+
raise ArgumentError, "Zero or negative precision for asin" if prec <= 0
264+
raise Math::DomainError, "Out of domain argument for asin" if x < -1 || x > 1
265+
return BigDecimal::NAN if x.nan?
266+
prec2 = prec + BigDecimal.double_fig
267+
cos = (1 - x**2).sqrt(prec2)
268+
if cos.zero?
269+
pi = PI(prec)
270+
x > 0 ? pi / 2 : -pi / 2
271+
else
272+
atan(x.div(cos, prec2), prec)
273+
end
274+
end
275+
276+
# call-seq:
277+
# acos(decimal, numeric) -> BigDecimal
278+
#
279+
# Computes the arccosine of +decimal+ to the specified number of digits of
280+
# precision, +numeric+.
281+
#
282+
# If +decimal+ is NaN, returns NaN.
283+
#
284+
# BigMath.acos(BigDecimal('0.5'), 16).to_s
285+
# #=> "0.10471975511965977461542144610932e1"
286+
#
287+
def acos(x, prec)
288+
raise ArgumentError, "Zero or negative precision for acos" if prec <= 0
289+
raise Math::DomainError, "Out of domain argument for acos" if x < -1 || x > 1
290+
291+
return PI(prec) / 2 - asin(x, prec) if x < 0
292+
return PI(prec) / 2 if x.zero?
293+
return BigDecimal::NAN if x.nan?
294+
295+
prec2 = prec + BigDecimal.double_fig
296+
sin = (1 - x**2).sqrt(prec2)
297+
atan(sin.div(x, prec2), prec)
298+
end
299+
249300
# call-seq:
250301
# atan(decimal, numeric) -> BigDecimal
251302
#
@@ -282,7 +333,7 @@ def atan(x, prec)
282333
y *= 2 if dbl
283334
y = pi / 2 - y if inv
284335
y = -y if neg
285-
y
336+
y.mult(1, n)
286337
end
287338

288339
# call-seq:

test/bigdecimal/test_bigmath.rb

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,39 @@ def test_tan
134134
assert_relative_precision {|n| tan(PI(200) * 100, n) }
135135
end
136136

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

0 commit comments

Comments
 (0)