Skip to content

Commit 2fbe7ff

Browse files
tompngmrkn
authored andcommitted
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 bf22f51 commit 2fbe7ff

2 files changed

Lines changed: 47 additions & 0 deletions

File tree

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)
@@ -134,6 +135,32 @@ def cos(x, prec)
134135
y < -1 ? BigDecimal("-1") : y > 1 ? BigDecimal("1") : y
135136
end
136137

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

test/bigdecimal/test_bigmath.rb

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,26 @@ def test_cos
8080
assert_operator(cos(PI(30) * 2, 30), :<=, 1)
8181
end
8282

83+
def test_tan
84+
assert_in_delta(0.0, tan(-PI(N), N))
85+
assert_in_delta(0.0, tan(BigDecimal(0), N))
86+
assert_in_delta(0.0, tan(PI(N), N))
87+
assert_in_delta(1.0, tan(PI(N) / 4, N))
88+
assert_in_delta(-1.0, tan(-PI(N) / 4, N))
89+
assert_in_delta(-1.0, tan(PI(N) * 3 / 4, N))
90+
assert_in_delta(1.0, tan(-PI(N) * 3 / 4, N))
91+
assert_in_delta(0.0, tan(PI(N) * 100, N))
92+
assert_in_delta(1.0, tan(PI(N) * 101 / 4, N))
93+
assert_in_delta(-1.0, tan(PI(N) * 103 / 4, N))
94+
assert_in_delta(BigDecimal("1").div(SQRT3, 100), tan(PI(100) / 6, 100), BigDecimal("1e-100"))
95+
assert_in_delta(SQRT3, tan(PI(100) / 3, 100), BigDecimal("1e-100"))
96+
assert_relative_precision {|n| tan(BigDecimal("0.5"), n) }
97+
assert_relative_precision {|n| tan(BigDecimal("1e-30"), n) }
98+
assert_relative_precision {|n| tan(BigDecimal("1.5"), n) }
99+
assert_relative_precision {|n| tan(PI(100) / 2, n) }
100+
assert_relative_precision {|n| tan(PI(200) * 101 / 2, n) }
101+
end
102+
83103
def test_atan
84104
assert_equal(0.0, atan(BigDecimal("0.0"), N))
85105
assert_in_delta(Math::PI/4, atan(BigDecimal("1.0"), N))

0 commit comments

Comments
 (0)