Skip to content

Commit 697e487

Browse files
committed
Implement asin and acos
1 parent fe099dd commit 697e487

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)
@@ -216,6 +218,55 @@ def tan(x, prec)
216218
sin.div(cos, prec + BigDecimal.double_fig)
217219
end
218220

221+
# call-seq:
222+
# asin(decimal, numeric) -> BigDecimal
223+
#
224+
# Computes the arcsine of +decimal+ to the specified number of digits of
225+
# precision, +numeric+.
226+
#
227+
# If +decimal+ is NaN, returns NaN.
228+
#
229+
# BigMath.asin(BigDecimal('0.5'), 16).to_s
230+
# #=> "0.52359877559829887307710723054659e0"
231+
#
232+
def asin(x, prec)
233+
raise ArgumentError, "Zero or negative precision for asin" if prec <= 0
234+
raise Math::DomainError, "Out of domain argument for asin" if x < -1 || x > 1
235+
return BigDecimal::NAN if x.nan?
236+
prec2 = prec + BigDecimal.double_fig
237+
cos = (1 - x**2).sqrt(prec2)
238+
if cos.zero?
239+
pi = PI(prec)
240+
x > 0 ? pi / 2 : -pi / 2
241+
else
242+
atan(x.div(cos, prec2), prec)
243+
end
244+
end
245+
246+
# call-seq:
247+
# acos(decimal, numeric) -> BigDecimal
248+
#
249+
# Computes the arccosine of +decimal+ to the specified number of digits of
250+
# precision, +numeric+.
251+
#
252+
# If +decimal+ is NaN, returns NaN.
253+
#
254+
# BigMath.acos(BigDecimal('0.5'), 16).to_s
255+
# #=> "0.10471975511965977461542144610932e1"
256+
#
257+
def acos(x, prec)
258+
raise ArgumentError, "Zero or negative precision for acos" if prec <= 0
259+
raise Math::DomainError, "Out of domain argument for acos" if x < -1 || x > 1
260+
261+
return PI(prec) / 2 - asin(x, prec) if x < 0
262+
return PI(prec) / 2 if x.zero?
263+
return BigDecimal::NAN if x.nan?
264+
265+
prec2 = prec + BigDecimal.double_fig
266+
sin = (1 - x**2).sqrt(prec2)
267+
atan(sin.div(x, prec2), prec)
268+
end
269+
219270
# call-seq:
220271
# atan(decimal, numeric) -> BigDecimal
221272
#
@@ -252,7 +303,7 @@ def atan(x, prec)
252303
y *= 2 if dbl
253304
y = pi / 2 - y if inv
254305
y = -y if neg
255-
y
306+
y.mult(1, n)
256307
end
257308

258309
# 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)