Skip to content

Commit fa5647e

Browse files
committed
Implement asin and acos
1 parent 45c4c62 commit fa5647e

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)
@@ -232,6 +234,55 @@ def tan(x, prec)
232234
sin(x, prec).div(cos, prec + BigDecimal.double_fig)
233235
end
234236

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

274325
# call-seq:

test/bigdecimal/test_bigmath.rb

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

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

0 commit comments

Comments
 (0)