Skip to content

Commit b947195

Browse files
committed
Implement BigMath.tan
Calculate `tan(x, prec)` by `sin(x, prec) / cos(x, denominator_prec)`. If the precision of cos is not enough, increase denominator_prec and recalculate cos again.
1 parent 8459a3c commit b947195

File tree

2 files changed

+47
-0
lines changed

2 files changed

+47
-0
lines changed

lib/bigdecimal/math.rb

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
# sqrt(x, prec)
88
# sin (x, prec)
99
# cos (x, prec)
10+
# tan (x, prec)
1011
# atan(x, prec)
1112
# PI (prec)
1213
# E (prec) == exp(1.0,prec)
@@ -131,6 +132,32 @@ def cos(x, prec)
131132
y
132133
end
133134

135+
# call-seq:
136+
# tan(decimal, numeric) -> BigDecimal
137+
#
138+
# Computes the tangent of +decimal+ to the specified number of digits of
139+
# precision, +numeric+.
140+
#
141+
# If +decimal+ is Infinity or NaN, returns NaN.
142+
#
143+
# BigMath.tan(BigMath.PI(16) / 3, 16).to_s
144+
# #=> "0.17320508075688772935274463415059e1"
145+
#
146+
def tan(x, prec)
147+
denominator_prec = prec + BigDecimal.double_fig
148+
while true
149+
cos = cos(x, denominator_prec)
150+
break if prec - cos.exponent <= denominator_prec
151+
152+
if cos.exponent == 0 || denominator_prec < -cos.exponent
153+
denominator_prec = denominator_prec * 3 / 2
154+
else
155+
denominator_prec = prec - cos.exponent + BigDecimal.double_fig
156+
end
157+
end
158+
sin(x, prec).div(cos, prec + BigDecimal.double_fig)
159+
end
160+
134161
# call-seq:
135162
# atan(decimal, numeric) -> BigDecimal
136163
#

test/bigdecimal/test_bigmath.rb

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,26 @@ def test_cos
7474
assert_fixed_point_precision {|n| cos(BigDecimal(PI(50) * 201 / 2), n) }
7575
end
7676

77+
def test_tan
78+
assert_in_delta(0.0, tan(-PI(N), N))
79+
assert_in_delta(0.0, tan(BigDecimal(0), N))
80+
assert_in_delta(0.0, tan(PI(N), N))
81+
assert_in_delta(1.0, tan(PI(N) / 4, N))
82+
assert_in_delta(-1.0, tan(-PI(N) / 4, N))
83+
assert_in_delta(-1.0, tan(PI(N) * 3 / 4, N))
84+
assert_in_delta(1.0, tan(-PI(N) * 3 / 4, N))
85+
assert_in_delta(0.0, tan(PI(N) * 100, N))
86+
assert_in_delta(1.0, tan(PI(N) * 101 / 4, N))
87+
assert_in_delta(-1.0, tan(PI(N) * 103 / 4, N))
88+
assert_in_delta(BigDecimal("1").div(SQRT3, 100), tan(PI(100) / 6, 100), BigDecimal("1e-100"))
89+
assert_in_delta(SQRT3, tan(PI(100) / 3, 100), BigDecimal("1e-100"))
90+
assert_relative_precision {|n| tan(BigDecimal("0.5"), n) }
91+
assert_relative_precision {|n| tan(BigDecimal("1e-30"), n) }
92+
assert_relative_precision {|n| tan(BigDecimal("1.5"), n) }
93+
assert_relative_precision {|n| tan(PI(100) / 2, n) }
94+
assert_relative_precision {|n| tan(PI(200) * 101 / 2, n) }
95+
end
96+
7797
def test_atan
7898
assert_equal(0.0, atan(BigDecimal("0.0"), N))
7999
assert_in_delta(Math::PI/4, atan(BigDecimal("1.0"), N))

0 commit comments

Comments
 (0)