flt 1.2.0 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +10 -0
- data/README.txt +25 -1
- data/lib/flt/bin_num.rb +1 -1
- data/lib/flt/math.rb +497 -370
- data/lib/flt/num.rb +7 -5
- data/lib/flt/version.rb +1 -1
- data/test/test_trig.rb +58 -11
- metadata +3 -3
data/History.txt
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
== 1.2.1 2010-06-16
|
2
|
+
|
3
|
+
* New features
|
4
|
+
- Trigonometry for BinNum and reorganization of Math modules.
|
5
|
+
|
6
|
+
* Bugfixes
|
7
|
+
- Context#elimit= didn't work
|
8
|
+
- atan could be inaccurate for small arguments
|
9
|
+
- asin could be inaccurate for arguments near 1
|
10
|
+
|
1
11
|
== 1.2.0 2010-06-15
|
2
12
|
|
3
13
|
* New Features
|
data/README.txt
CHANGED
@@ -498,6 +498,29 @@ Note also that if we normalize a value we will change it's precision to that of
|
|
498
498
|
puts x.normalize.number_of_digits # -> 53
|
499
499
|
puts x.normalize.to_s # -> 0.125
|
500
500
|
|
501
|
+
== Mathematical functions
|
502
|
+
|
503
|
+
There are two mathematical functions modules analogous to Ruby's Math for Float,
|
504
|
+
Flt::DecNum::Math and Flt::BinNum::Math.
|
505
|
+
Currently they consist of basic trigonometric functions, including hypot, and the
|
506
|
+
constants e and pi.
|
507
|
+
|
508
|
+
Its functions can be accessed in a number of ways:
|
509
|
+
|
510
|
+
require 'flt/math'
|
511
|
+
DecNum.context(:precision=>10) do |context|
|
512
|
+
# As module functions:
|
513
|
+
puts DecNum::Math.sin(1)*DecNum::Math.pi # -> 2.643559064
|
514
|
+
# As functions of a context object:
|
515
|
+
puts context.sin(1)*context.pi # -> 2.643559064
|
516
|
+
# Through a math block:
|
517
|
+
puts DecNum.context.math{sin(1)*pi} # -> 2.643559064
|
518
|
+
puts DecNum.math{sin(1)*pi} # -> 2.643559064
|
519
|
+
# And can be included to be used ans private instance methods:
|
520
|
+
include DecNum::Math
|
521
|
+
puts sin(1)*pi # -> 2.643559064
|
522
|
+
end
|
523
|
+
|
501
524
|
== More Information
|
502
525
|
|
503
526
|
* Decimal Floating point type: see the base Flt::Num class and the Flt::DecNum class
|
@@ -507,6 +530,7 @@ Note also that if we normalize a value we will change it's precision to that of
|
|
507
530
|
* Floating Point Tolerance: see the flt/tolerance.rb[link:files/lib/flt/tolerance_rb.html] file
|
508
531
|
and the Flt::Tolerance class
|
509
532
|
* Constructors: see Flt.DecNum(), Flt.BinNum() and Flt.Tolerance().
|
533
|
+
* Mathematical functions: see Flt::MathBase.
|
510
534
|
|
511
535
|
= DecNum vs BigDecimal
|
512
536
|
|
@@ -567,7 +591,7 @@ EXPAND+
|
|
567
591
|
|
568
592
|
= Roadmap
|
569
593
|
|
570
|
-
*
|
594
|
+
* Trigonometry optimizations
|
571
595
|
* Complex support.
|
572
596
|
* Implement the missing GDA functions:
|
573
597
|
rotate, shift, trim, and, or, xor, invert,
|
data/lib/flt/bin_num.rb
CHANGED
data/lib/flt/math.rb
CHANGED
@@ -1,460 +1,587 @@
|
|
1
1
|
require 'flt/dec_num'
|
2
2
|
|
3
3
|
module Flt
|
4
|
-
class DecNum
|
5
|
-
module Math
|
6
4
|
|
7
|
-
|
5
|
+
# Mathematical functions. The angular units used by these functions can be specified
|
6
|
+
# with the +angle+ attribute of the context. The accepted values are:
|
7
|
+
# * :rad for radians
|
8
|
+
# * :deg for degrees
|
9
|
+
# * :grad for gradians
|
10
|
+
module MathBase
|
8
11
|
|
9
|
-
|
12
|
+
# Cosine of an angle given in the units specified by DecNum.context.angle.
|
13
|
+
def cos(x)
|
14
|
+
cos_base(num_class.Num(x))
|
15
|
+
end
|
10
16
|
|
11
|
-
|
17
|
+
# Sine of an angle given in the units specified by DecNum.context.angle.
|
18
|
+
def sin(x)
|
19
|
+
sin_base(num_class.Num(x))
|
20
|
+
end
|
12
21
|
|
13
|
-
|
22
|
+
# Tangent of an angle given in the units specified by DecNum.context.angle.
|
23
|
+
def tan(x)
|
24
|
+
tan_base(num_class.Num(x))
|
25
|
+
end
|
14
26
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
27
|
+
# Arc-tangent. The result is in the units specified by DecNum.context.angle.
|
28
|
+
# If the angular units are radians the result is in [-pi/2, pi/2]; it is in [-90,90] in degrees.
|
29
|
+
def atan(x)
|
30
|
+
atan_base(num_class.Num(x))
|
31
|
+
end
|
20
32
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
digits += margin + 1
|
28
|
-
fudge = 10
|
29
|
-
unity = 10**(digits+fudge)
|
30
|
-
v = 4*(4*iarccot(5, unity) - iarccot(239, unity))
|
31
|
-
v = v.to_s[0,digits]
|
32
|
-
# if the last digit is 0 or 5 the truncated value may not be good for rounding
|
33
|
-
loop do
|
34
|
-
#last_digit = v%10
|
35
|
-
last_digit = v[-1,1].to_i
|
36
|
-
continue = (last_digit==5 || last_digit==0)
|
37
|
-
if continue && margin>0
|
38
|
-
# if we have margin we back-up one digit
|
39
|
-
margin -= 1
|
40
|
-
v = v[0...-1]
|
41
|
-
else
|
42
|
-
break
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
@@pi_cache_digits = digits + margin - PI_MARGIN # @pi_cache.size
|
47
|
-
@@pi_cache = v # DecNum(+1, v, 1-digits) # cache truncated value
|
48
|
-
end
|
49
|
-
# Now we avoid rounding too much because it is slow
|
50
|
-
l = round_digits + 1
|
51
|
-
while (l<@@pi_cache_digits) && [0,5].include?(@@pi_cache[l-1,1].to_i)
|
52
|
-
l += 1
|
53
|
-
end
|
54
|
-
v = @@pi_cache[0,l]
|
55
|
-
DecNum.context(:precision=>round_digits){+DecNum(+1,v.to_i,1-l)}
|
56
|
-
end
|
33
|
+
# Arc-tangent with two arguments (principal value of the argument of the complex number x+i*y).
|
34
|
+
# The result is in the units specified by DecNum.context.angle.
|
35
|
+
# If the angular units are radians the result is in [-pi, pi]; it is in [-180,180] in degrees.
|
36
|
+
def atan2(y, x)
|
37
|
+
atan2_base(num_class.Num(y), num_class.Num(x))
|
38
|
+
end
|
57
39
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
end
|
40
|
+
# Arc-sine. The result is in the units specified by DecNum.context.angle.
|
41
|
+
# If the angular units are radians the result is in [-pi/2, pi/2]; it is in [-90,90] in degrees.
|
42
|
+
def asin(x)
|
43
|
+
asin_base(num_class.Num(x))
|
44
|
+
end
|
64
45
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
x = x.abs
|
71
|
-
rev_sign = false
|
72
|
-
s = nil
|
73
|
-
DecNum.context do |local_context|
|
74
|
-
local_context.precision += 3 # extra digits for intermediate steps
|
75
|
-
x,k,pi_2 = reduce_angle2(x,2)
|
76
|
-
rev_sign = true if k>1
|
77
|
-
if k % 2 == 0
|
78
|
-
x = pi_2 - x
|
79
|
-
else
|
80
|
-
rev_sign = !rev_sign
|
81
|
-
end
|
82
|
-
x = to_rad(x)
|
83
|
-
i, lasts, fact, num = 1, 0, 1, DecNum(x)
|
84
|
-
s = num
|
85
|
-
x2 = -x*x
|
86
|
-
while s != lasts
|
87
|
-
lasts = s
|
88
|
-
i += 2
|
89
|
-
fact *= i * (i-1)
|
90
|
-
num *= x2
|
91
|
-
s += num / fact
|
92
|
-
end
|
93
|
-
end
|
94
|
-
return rev_sign ? (-s) : (+s)
|
95
|
-
end
|
46
|
+
# Arc-cosine. The result is in the units specified by DecNum.context.angle.
|
47
|
+
# If the angular units are radians the result is in [-pi/2, pi/2]; it is in [-90,90] in degrees.
|
48
|
+
def acos(x)
|
49
|
+
acos_base(num_class.Num(x))
|
50
|
+
end
|
96
51
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
def sin(x)
|
102
|
-
sign = x.sign
|
103
|
-
s = nil
|
104
|
-
DecNum.context do |local_context|
|
105
|
-
local_context.precision += 3 # extra digits for intermediate steps
|
106
|
-
x = x.abs if sign<0
|
107
|
-
x,k,pi_2 = reduce_angle2(x,2)
|
108
|
-
sign = -sign if k>1
|
109
|
-
x = pi_2 - x if k % 2 == 1
|
110
|
-
x = to_rad(x)
|
111
|
-
i, lasts, fact, num = 1, 0, 1, DecNum(x)
|
112
|
-
s = num
|
113
|
-
x2 = -x*x
|
114
|
-
while s != lasts
|
115
|
-
lasts = s
|
116
|
-
i += 2
|
117
|
-
fact *= i * (i-1)
|
118
|
-
num *= x2
|
119
|
-
s += num / fact
|
120
|
-
end
|
121
|
-
end
|
122
|
-
return (+s).copy_sign(sign)
|
123
|
-
end
|
52
|
+
# Length of the hypotenuse of a right-angle triangle (modulus or absolute value of the complex x+i*y).
|
53
|
+
def hypot(x, y)
|
54
|
+
hypot_base(num_class.Num(x), num_class.Num(y))
|
55
|
+
end
|
124
56
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
57
|
+
private
|
58
|
+
|
59
|
+
def cos_base(x)
|
60
|
+
x = x.abs
|
61
|
+
rev_sign = false
|
62
|
+
s = nil
|
63
|
+
num_class.context do |local_context|
|
64
|
+
local_context.precision += 3 # extra digits for intermediate steps
|
65
|
+
x,k,pi_2 = reduce_angle2(x,2)
|
66
|
+
rev_sign = true if k>1
|
67
|
+
if k % 2 == 0
|
68
|
+
x = pi_2 - x
|
69
|
+
else
|
70
|
+
rev_sign = !rev_sign
|
71
|
+
end
|
72
|
+
x = to_rad(x)
|
73
|
+
i, lasts, fact, num = 1, 0, 1, num_class.Num(x)
|
74
|
+
s = num
|
75
|
+
x2 = -x*x
|
76
|
+
while s != lasts
|
77
|
+
lasts = s
|
78
|
+
i += 2
|
79
|
+
fact *= i * (i-1)
|
80
|
+
num *= x2
|
81
|
+
s += num / fact
|
131
82
|
end
|
132
83
|
end
|
84
|
+
return rev_sign ? (-s) : (+s)
|
85
|
+
end
|
133
86
|
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
local_context.precision += 2
|
154
|
-
x_squared = x ** 2
|
155
|
-
y = x_squared / (1 + x_squared)
|
156
|
-
y_over_x = y / x
|
157
|
-
i = DecNum.zero; lasts = 0; s = y_over_x; coeff = 1; num = y_over_x
|
158
|
-
while s != lasts
|
159
|
-
lasts = s
|
160
|
-
i += 2
|
161
|
-
coeff *= i / (i + 1)
|
162
|
-
num *= y
|
163
|
-
s += coeff * num
|
164
|
-
end
|
165
|
-
if c && c!= 0
|
166
|
-
s = c - s
|
167
|
-
end
|
87
|
+
def sin_base(x)
|
88
|
+
sign = x.sign
|
89
|
+
s = nil
|
90
|
+
num_class.context do |local_context|
|
91
|
+
local_context.precision += 3 # extra digits for intermediate steps
|
92
|
+
x = x.abs if sign<0
|
93
|
+
x,k,pi_2 = reduce_angle2(x,2)
|
94
|
+
sign = -sign if k>1
|
95
|
+
x = pi_2 - x if k % 2 == 1
|
96
|
+
x = to_rad(x)
|
97
|
+
i, lasts, fact, num = 1, 0, 1, num_class.Num(x)
|
98
|
+
s = num
|
99
|
+
x2 = -x*x
|
100
|
+
while s != lasts
|
101
|
+
lasts = s
|
102
|
+
i += 2
|
103
|
+
fact *= i * (i-1)
|
104
|
+
num *= x2
|
105
|
+
s += num / fact
|
168
106
|
end
|
169
|
-
return conversion ? rad_to(s) : +s
|
170
107
|
end
|
108
|
+
return (+s).copy_sign(sign)
|
109
|
+
end
|
171
110
|
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
a = y!=0 ? atan(y / x) : DecNum.zero
|
180
|
-
a += half_cycle.copy_sign(y) if x < 0
|
181
|
-
return a
|
182
|
-
elsif abs_y == abs_x
|
183
|
-
x = DecNum(1).copy_sign(x)
|
184
|
-
y = DecNum(1).copy_sign(y)
|
185
|
-
return half_cycle * (2 - x) / (4 * y)
|
186
|
-
end
|
187
|
-
end
|
111
|
+
def tan_base(x)
|
112
|
+
+num_class.context do |local_context|
|
113
|
+
local_context.precision += 2 # extra digits for intermediate steps
|
114
|
+
s,c = sin(x), cos(x)
|
115
|
+
s/c
|
116
|
+
end
|
117
|
+
end
|
188
118
|
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
119
|
+
def atan_base(x)
|
120
|
+
s = nil
|
121
|
+
conversion = true
|
122
|
+
extra_prec = num_class.radix==2 ? 4 : 2
|
123
|
+
num_class.context do |local_context|
|
124
|
+
local_context.precision += extra_prec
|
125
|
+
if x == 0
|
126
|
+
return DecNum.zero
|
127
|
+
elsif x.abs > 1
|
128
|
+
if x.infinite?
|
129
|
+
s = (quarter_cycle).copy_sign(x)
|
130
|
+
conversion = false
|
131
|
+
break
|
193
132
|
else
|
194
|
-
|
133
|
+
# c = (quarter_cycle).copy_sign(x)
|
134
|
+
c = (half*pi).copy_sign(x)
|
135
|
+
x = 1 / x
|
195
136
|
end
|
137
|
+
end
|
138
|
+
local_context.precision += extra_prec
|
139
|
+
x_squared = x ** 2
|
140
|
+
if x_squared.zero? || x_squared.subnormal?
|
141
|
+
s = x
|
142
|
+
s = c - s if c && c!=0
|
143
|
+
break
|
144
|
+
end
|
145
|
+
y = x_squared / (1 + x_squared)
|
146
|
+
y_over_x = y / x
|
147
|
+
i = num_class.zero; lasts = 0; s = y_over_x; coeff = 1; num = y_over_x
|
148
|
+
while s != lasts
|
149
|
+
lasts = s
|
150
|
+
i += 2
|
151
|
+
coeff *= i / (i + 1)
|
152
|
+
num *= y
|
153
|
+
s += coeff * num
|
154
|
+
end
|
155
|
+
if c && c!= 0
|
156
|
+
s = c - s
|
157
|
+
end
|
196
158
|
end
|
159
|
+
return conversion ? rad_to(s) : +s
|
160
|
+
end
|
197
161
|
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
end
|
215
|
-
+x
|
216
|
-
end
|
162
|
+
def atan2_base(y, x)
|
163
|
+
abs_y = y.abs
|
164
|
+
abs_x = x.abs
|
165
|
+
y_is_real = !x.infinite?
|
166
|
+
|
167
|
+
if x != 0
|
168
|
+
if y_is_real
|
169
|
+
a = y!=0 ? atan(y / x) : num_class.zero
|
170
|
+
a += half_cycle.copy_sign(y) if x < 0
|
171
|
+
return a
|
172
|
+
elsif abs_y == abs_x
|
173
|
+
x = num_class.Num(1).copy_sign(x)
|
174
|
+
y = num_class.Num(1).copy_sign(y)
|
175
|
+
return half_cycle * (2 - x) / (4 * y)
|
176
|
+
end
|
177
|
+
end
|
217
178
|
|
218
|
-
|
179
|
+
if y != 0
|
180
|
+
return atan(num_class.infinity(y.sign))
|
181
|
+
elsif x < 0
|
182
|
+
return half_cycle.copy_sign(x)
|
183
|
+
else
|
184
|
+
return num_class.zero
|
185
|
+
end
|
186
|
+
end
|
219
187
|
|
220
|
-
|
188
|
+
def asin_base(x)
|
189
|
+
x = +x
|
190
|
+
return num_class.context.exception(Num::InvalidOperation, 'asin needs -1 <= x <= 1') if x.abs > 1
|
221
191
|
|
222
192
|
if x == -1
|
223
|
-
return
|
193
|
+
return -quarter_cycle
|
224
194
|
elsif x == 0
|
225
|
-
return
|
195
|
+
return num_class.zero
|
226
196
|
elsif x == 1
|
227
|
-
return
|
197
|
+
return quarter_cycle
|
228
198
|
end
|
229
199
|
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
x = quarter_cycle - atan(x)
|
235
|
-
end
|
236
|
-
else
|
237
|
-
# valid for x>=0
|
238
|
-
DecNum.context do |local_context|
|
239
|
-
local_context.precision += 3
|
240
|
-
x = (1-x*x).sqrt
|
241
|
-
x = asin(x)
|
242
|
-
end
|
200
|
+
num_class.context do |local_context|
|
201
|
+
local_context.precision += 3
|
202
|
+
x = x/(1-x*x).sqrt
|
203
|
+
x = atan(x)
|
243
204
|
end
|
244
205
|
+x
|
206
|
+
end
|
207
|
+
|
208
|
+
def acos_base(x)
|
245
209
|
|
210
|
+
return num_class.context.exception(Num::InvalidOperation, 'acos needs -1 <= x <= 2') if x.abs > 1
|
211
|
+
|
212
|
+
if x == -1
|
213
|
+
return half_cycle
|
214
|
+
elsif x == 0
|
215
|
+
return quarter_cycle
|
216
|
+
elsif x == 1
|
217
|
+
return num_class.zero
|
246
218
|
end
|
247
219
|
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
220
|
+
required_precision = num_class.context.precision
|
221
|
+
|
222
|
+
if x < half
|
223
|
+
num_class.context(:precision=>required_precision+2) do
|
224
|
+
x = x/(1-x*x).sqrt
|
225
|
+
x = quarter_cycle - atan(x)
|
252
226
|
end
|
253
|
-
|
227
|
+
else
|
228
|
+
# valid for x>=0
|
229
|
+
num_class.context(:precision=>required_precision+3) do
|
254
230
|
|
255
|
-
|
231
|
+
# x = (1-x*x).sqrt # x*x may require double precision if x*x is near 1
|
232
|
+
x = (1-BinNum.context(:precision=>required_precision*2){x*x}).sqrt
|
256
233
|
|
257
|
-
|
258
|
-
decimals ||= DecNum.context.precision
|
259
|
-
DecNum.context(:precision=>decimals) do
|
260
|
-
pi(decimals)*2
|
234
|
+
x = asin(x)
|
261
235
|
end
|
262
236
|
end
|
237
|
+
+x
|
238
|
+
|
239
|
+
end
|
240
|
+
|
241
|
+
def hypot_base(x, y)
|
242
|
+
num_class.context do |local_context|
|
243
|
+
local_context.precision += 3
|
244
|
+
(x*x + y*y).sqrt
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
def e(digits=nil)
|
249
|
+
num_class.context do |local_context|
|
250
|
+
local_context.precision = digits if digits
|
251
|
+
num_class.Num(1).exp
|
252
|
+
end
|
253
|
+
end
|
263
254
|
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
255
|
+
def pi2(decimals=nil)
|
256
|
+
decimals ||= DecNum.context.precision
|
257
|
+
num_class.context(:precision=>decimals) do
|
258
|
+
pi(decimals)*2
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
def invpi(decimals=nil)
|
263
|
+
decimals ||= DecNum.context.precision
|
264
|
+
num_class.context(:precision=>decimals) do
|
265
|
+
num_class.Num(1)/pi(decimals)
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
def inv2pi(decimals=nil)
|
270
|
+
decimals ||= DecNum.context.precision
|
271
|
+
num_class.context(:precision=>decimals) do
|
272
|
+
num_class.Num(1)/pi2(decimals)
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
# class <<self
|
277
|
+
# private
|
278
|
+
|
279
|
+
def iarccot(x, unity)
|
280
|
+
xpow = unity / x
|
281
|
+
n = 1
|
282
|
+
sign = 1
|
283
|
+
sum = 0
|
284
|
+
loop do
|
285
|
+
term = xpow / n
|
286
|
+
break if term == 0
|
287
|
+
sum += sign * (xpow/n)
|
288
|
+
xpow /= x*x
|
289
|
+
n += 2
|
290
|
+
sign = -sign
|
268
291
|
end
|
292
|
+
sum
|
293
|
+
end
|
294
|
+
|
295
|
+
def modtwopi(x)
|
296
|
+
return +num_class.context(:precision=>num_class.context.precision*3){x.modulo(one_cycle)}
|
269
297
|
end
|
270
298
|
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
299
|
+
# Reduce angle to [0,2Pi)
|
300
|
+
def reduce_angle(a)
|
301
|
+
modtwopi(a)
|
302
|
+
end
|
303
|
+
|
304
|
+
# Reduce angle to [0,Pi/k0) (result is not rounded to precision)
|
305
|
+
def reduce_angle2(a,k0=nil) # divisor of pi or nil for pi*2
|
306
|
+
# we could reduce first to pi*2 to avoid the mod k0 operation
|
307
|
+
k,r,divisor = DecNum.context do
|
308
|
+
num_class.context.precision *= 3
|
309
|
+
m = k0.nil? ? one_cycle : half_cycle/k0
|
310
|
+
a.divmod(m)+[m]
|
275
311
|
end
|
312
|
+
[r, k.modulo(k0*2).to_i, divisor]
|
276
313
|
end
|
277
314
|
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
loop do
|
287
|
-
term = xpow / n
|
288
|
-
break if term == 0
|
289
|
-
sum += sign * (xpow/n)
|
290
|
-
xpow /= x*x
|
291
|
-
n += 2
|
292
|
-
sign = -sign
|
293
|
-
end
|
294
|
-
sum
|
315
|
+
def one_cycle
|
316
|
+
case num_class.context.angle
|
317
|
+
when :rad
|
318
|
+
pi2
|
319
|
+
when :deg
|
320
|
+
num_class.Num(360)
|
321
|
+
when :grad
|
322
|
+
num_class.Num(400)
|
295
323
|
end
|
324
|
+
end
|
296
325
|
|
297
|
-
|
298
|
-
|
326
|
+
def half_cycle
|
327
|
+
case num_class.context.angle
|
328
|
+
when :rad
|
329
|
+
pi
|
330
|
+
when :deg
|
331
|
+
num_class.Num(180)
|
332
|
+
when :grad
|
333
|
+
num_class.Num(200)
|
299
334
|
end
|
335
|
+
end
|
300
336
|
|
301
|
-
|
302
|
-
|
303
|
-
|
337
|
+
def quarter_cycle
|
338
|
+
case DecNum.context.angle
|
339
|
+
when :rad
|
340
|
+
half*pi
|
341
|
+
when :deg
|
342
|
+
num_class.Num(90)
|
343
|
+
when :grad
|
344
|
+
num_class.Num(100)
|
304
345
|
end
|
346
|
+
end
|
305
347
|
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
a.divmod(m)+[m]
|
313
|
-
end
|
314
|
-
[r, k.modulo(k0*2).to_i, divisor]
|
348
|
+
def to_rad(x)
|
349
|
+
case num_class.context.angle
|
350
|
+
when :rad
|
351
|
+
+x
|
352
|
+
else
|
353
|
+
+num_class.context(:precision=>num_class.context.precision+3){x*pi/half_cycle}
|
315
354
|
end
|
355
|
+
end
|
316
356
|
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
when :grad
|
324
|
-
DecNum(400)
|
325
|
-
end
|
357
|
+
def to_deg(x)
|
358
|
+
case num_class.context.angle
|
359
|
+
when :deg
|
360
|
+
+x
|
361
|
+
else
|
362
|
+
+num_class.context(:precision=>num_class.context.precision+3){x*num_class.Num(180)/half_cycle}
|
326
363
|
end
|
364
|
+
end
|
327
365
|
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
when :grad
|
335
|
-
DecNum(200)
|
336
|
-
end
|
366
|
+
def to_grad(x)
|
367
|
+
case DecNum.context.angle
|
368
|
+
when :deg
|
369
|
+
+x
|
370
|
+
else
|
371
|
+
+num_class.context(:precision=>num_class.context.precision+3){x*num_class.Num(200)/half_cycle}
|
337
372
|
end
|
373
|
+
end
|
338
374
|
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
375
|
+
def to_angle(angular_units, x)
|
376
|
+
return +x if angular_units == num_class.context.angle
|
377
|
+
case angular_units
|
378
|
+
when :rad
|
379
|
+
to_rad(x)
|
380
|
+
when :deg
|
381
|
+
to_deg(x)
|
382
|
+
when :grad
|
383
|
+
to_grad(x)
|
348
384
|
end
|
385
|
+
end
|
349
386
|
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
end
|
387
|
+
def rad_to(x)
|
388
|
+
case num_class.context.angle
|
389
|
+
when :rad
|
390
|
+
+x
|
391
|
+
else
|
392
|
+
+num_class.context(:precision=>num_class.context.precision+3){x*half_cycle/pi}
|
357
393
|
end
|
394
|
+
end
|
358
395
|
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
end
|
396
|
+
def deg_to(x)
|
397
|
+
case num_class.context.angle
|
398
|
+
when :deg
|
399
|
+
+x
|
400
|
+
else
|
401
|
+
+num_class.context(:precision=>num_class.context.precision+3){x*half_cycle/num_class.Num(180)}
|
366
402
|
end
|
403
|
+
end
|
367
404
|
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
end
|
405
|
+
def grad_to(x)
|
406
|
+
case num_class.context.angle
|
407
|
+
when :grad
|
408
|
+
+x
|
409
|
+
else
|
410
|
+
+num_class.context(:precision=>num_class.context.precision+3){x*half_cycle/num_class.Num(200)}
|
375
411
|
end
|
412
|
+
end
|
376
413
|
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
end
|
414
|
+
def angle_to(x, angular_units)
|
415
|
+
return +x if angular_units == num_class.context.angle
|
416
|
+
case angular_units
|
417
|
+
when :rad
|
418
|
+
rad_to(x)
|
419
|
+
when :deg
|
420
|
+
deg_to(x)
|
421
|
+
when :grad
|
422
|
+
grad_to(x)
|
387
423
|
end
|
424
|
+
end
|
388
425
|
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
426
|
+
#end
|
427
|
+
|
428
|
+
end
|
429
|
+
|
430
|
+
|
431
|
+
class DecNum
|
432
|
+
|
433
|
+
module Math
|
434
|
+
|
435
|
+
extend Flt # to access constructor methods DecNum
|
436
|
+
|
437
|
+
include MathBase # make available for instance methods
|
438
|
+
extend MathBase # make available for class methods
|
439
|
+
|
440
|
+
module Support
|
441
|
+
#private
|
442
|
+
def num_class
|
443
|
+
DecNum
|
396
444
|
end
|
397
445
|
|
398
|
-
def
|
399
|
-
|
400
|
-
when :deg
|
401
|
-
+x
|
402
|
-
else
|
403
|
-
+DecNum.context(:precision=>DecNum.context.precision+3){x*half_cycle/DecNum(180)}
|
404
|
-
end
|
446
|
+
def half
|
447
|
+
num_class.Num('0.5')
|
405
448
|
end
|
449
|
+
end
|
406
450
|
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
451
|
+
include Support
|
452
|
+
extend Support
|
453
|
+
|
454
|
+
module_function
|
455
|
+
|
456
|
+
|
457
|
+
# Pi
|
458
|
+
@@pi_cache = nil # truncated pi digits as a string
|
459
|
+
@@pi_cache_digits = 0
|
460
|
+
PI_MARGIN = 10
|
461
|
+
def pi(round_digits=nil)
|
462
|
+
|
463
|
+
round_digits ||= DecNum.context.precision
|
464
|
+
digits = round_digits
|
465
|
+
if @@pi_cache_digits <= digits # we need at least one more truncated digit
|
466
|
+
continue = true
|
467
|
+
while continue
|
468
|
+
margin = PI_MARGIN # margin to reduce recomputing with more digits to avoid ending in 0 or 5
|
469
|
+
digits += margin + 1
|
470
|
+
fudge = 10
|
471
|
+
unity = 10**(digits+fudge)
|
472
|
+
v = 4*(4*iarccot(5, unity) - iarccot(239, unity))
|
473
|
+
v = v.to_s[0,digits]
|
474
|
+
# if the last digit is 0 or 5 the truncated value may not be good for rounding
|
475
|
+
loop do
|
476
|
+
#last_digit = v%10
|
477
|
+
last_digit = v[-1,1].to_i
|
478
|
+
continue = (last_digit==5 || last_digit==0)
|
479
|
+
if continue && margin>0
|
480
|
+
# if we have margin we back-up one digit
|
481
|
+
margin -= 1
|
482
|
+
v = v[0...-1]
|
483
|
+
else
|
484
|
+
break
|
485
|
+
end
|
486
|
+
end
|
487
|
+
end
|
488
|
+
@@pi_cache_digits = digits + margin - PI_MARGIN # @pi_cache.size
|
489
|
+
@@pi_cache = v # DecNum(+1, v, 1-digits) # cache truncated value
|
413
490
|
end
|
491
|
+
# Now we avoid rounding too much because it is slow
|
492
|
+
l = round_digits + 1
|
493
|
+
while (l<@@pi_cache_digits) && [0,5].include?(@@pi_cache[l-1,1].to_i)
|
494
|
+
l += 1
|
495
|
+
end
|
496
|
+
v = @@pi_cache[0,l]
|
497
|
+
num_class.context(:precision=>round_digits){+num_class.Num(+1,v.to_i,1-l)}
|
498
|
+
end
|
499
|
+
|
500
|
+
|
501
|
+
end # DecNum::Math
|
502
|
+
|
503
|
+
class DecNum::Context
|
504
|
+
include DecNum::Math
|
505
|
+
public :sin, :cos, :tan, :atan, :asin, :acos, :atan2, :hypot, :pi, :e
|
506
|
+
end
|
507
|
+
|
508
|
+
def self.pi
|
509
|
+
self::Math.pi
|
510
|
+
end
|
511
|
+
|
512
|
+
def self.e
|
513
|
+
self::Math.e
|
514
|
+
end
|
515
|
+
|
516
|
+
end # DecNum
|
517
|
+
|
518
|
+
class BinNum
|
519
|
+
|
520
|
+
module Math
|
521
|
+
|
522
|
+
extend Flt # to access constructor methods DecNum
|
523
|
+
|
524
|
+
include MathBase # make available for instance methods
|
525
|
+
extend MathBase # make available for class methods
|
526
|
+
|
527
|
+
module Support
|
528
|
+
#private
|
529
|
+
def num_class
|
530
|
+
BinNum
|
414
531
|
end
|
415
532
|
|
416
|
-
def
|
417
|
-
|
418
|
-
case angular_units
|
419
|
-
when :rad
|
420
|
-
rad_to(x)
|
421
|
-
when :deg
|
422
|
-
deg_to(x)
|
423
|
-
when :grad
|
424
|
-
grad_to(x)
|
425
|
-
end
|
533
|
+
def half
|
534
|
+
num_class.Num('0.5')
|
426
535
|
end
|
536
|
+
end
|
427
537
|
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
#
|
441
|
-
|
442
|
-
|
443
|
-
|
538
|
+
include Support
|
539
|
+
extend Support
|
540
|
+
|
541
|
+
module_function
|
542
|
+
|
543
|
+
# Pi
|
544
|
+
@@pi = nil
|
545
|
+
@@pi_cache = [num_class.Num(3), 3, 1, 0, 0, 24]
|
546
|
+
@@pi_digits = 0
|
547
|
+
def pi(round_digits=nil)
|
548
|
+
round_digits ||= num_class.context.precision
|
549
|
+
if @@pi_digits < round_digits
|
550
|
+
# provisional implementation (very slow)
|
551
|
+
lasts = 0
|
552
|
+
t, s, n, na, d, da = @@pi_cache
|
553
|
+
num_class.context do |local_context|
|
554
|
+
local_context.precision = round_digits + 6
|
555
|
+
while s != lasts
|
556
|
+
lasts = s
|
557
|
+
n, na = n+na, na+8
|
558
|
+
d, da = d+da, da+32
|
559
|
+
t = (t * n) / d
|
560
|
+
s += t
|
561
|
+
end
|
444
562
|
end
|
563
|
+
@pi_cache = [t, s, n, na, d, da]
|
564
|
+
@@pi = s
|
565
|
+
@@pi_digits = round_digits
|
445
566
|
end
|
567
|
+
num_class.context(:precision=>round_digits){+@@pi}
|
446
568
|
end
|
447
|
-
end
|
448
569
|
|
449
|
-
|
570
|
+
end # BinNum::Math
|
450
571
|
|
451
|
-
|
452
|
-
Math
|
572
|
+
class BinNum::Context
|
573
|
+
include BinNum::Math
|
574
|
+
public :sin, :cos, :tan, :atan, :asin, :acos, :atan2, :hypot, :pi, :e
|
453
575
|
end
|
454
576
|
|
455
|
-
def
|
456
|
-
Math.
|
577
|
+
def self.pi
|
578
|
+
self::Math.pi
|
457
579
|
end
|
458
580
|
|
459
|
-
|
581
|
+
def self.e
|
582
|
+
self::Math.e
|
583
|
+
end
|
584
|
+
|
585
|
+
end # BinNum
|
586
|
+
|
460
587
|
end # Flt
|
data/lib/flt/num.rb
CHANGED
@@ -519,7 +519,7 @@ class Num < Numeric
|
|
519
519
|
# if e > 0 it is taken as emax and emin=1-emax
|
520
520
|
# if e < 0 it is taken as emin and emax=1-emin
|
521
521
|
def elimit=(e)
|
522
|
-
@emin, @emax = [
|
522
|
+
@emin, @emax = [e, 1-e].sort
|
523
523
|
end
|
524
524
|
|
525
525
|
# synonym for precision()
|
@@ -1261,10 +1261,7 @@ class Num < Numeric
|
|
1261
1261
|
def nan()
|
1262
1262
|
new [+1, nil, :nan]
|
1263
1263
|
end
|
1264
|
-
end
|
1265
|
-
|
1266
1264
|
|
1267
|
-
class <<self
|
1268
1265
|
def int_radix_power(n)
|
1269
1266
|
self.radix**n
|
1270
1267
|
end
|
@@ -1276,6 +1273,11 @@ class Num < Numeric
|
|
1276
1273
|
def int_div_radix_power(x,n)
|
1277
1274
|
n < 0 ? (x * self.radix**(-n) ) : (x / self.radix**n)
|
1278
1275
|
end
|
1276
|
+
|
1277
|
+
def math(*args, &blk)
|
1278
|
+
self.context.math(*args, &blk)
|
1279
|
+
end
|
1280
|
+
|
1279
1281
|
end
|
1280
1282
|
|
1281
1283
|
# A floating point-number value can be defined by:
|
@@ -4068,7 +4070,7 @@ class Num < Numeric
|
|
4068
4070
|
def log2_lb(c)
|
4069
4071
|
raise ArgumentError, "The argument to _log2_lb should be nonnegative." if c <= 0
|
4070
4072
|
str_c = c.to_s(16)
|
4071
|
-
return LOG2_MULT*4*str_c.length - LOG2_LB_CORRECTION[str_c[0,1].to_i(16)]
|
4073
|
+
return LOG2_MULT*4*str_c.length - LOG2_LB_CORRECTION[str_c[0,1].to_i(16)-1]
|
4072
4074
|
end
|
4073
4075
|
|
4074
4076
|
LOG10_MULT = 100
|
data/lib/flt/version.rb
CHANGED
data/test/test_trig.rb
CHANGED
@@ -1,8 +1,6 @@
|
|
1
1
|
require File.expand_path(File.join(File.dirname(__FILE__),'helper.rb'))
|
2
2
|
require File.dirname(__FILE__) + '/../lib/flt/math'
|
3
3
|
|
4
|
-
# TODO: Currently tests only with 12 digits of precision; test with more precision.
|
5
|
-
|
6
4
|
class TestTrig < Test::Unit::TestCase
|
7
5
|
|
8
6
|
|
@@ -14,14 +12,16 @@ class TestTrig < Test::Unit::TestCase
|
|
14
12
|
radix = $2.to_i
|
15
13
|
prec = $3.to_i
|
16
14
|
angle = $4.to_sym
|
15
|
+
num_class = Flt::Num[radix]
|
17
16
|
@data[radix] ||= {}
|
18
17
|
@data[radix][angle] ||= {}
|
19
18
|
@data[radix][angle][prec] ||= {}
|
20
19
|
@data[radix][angle][prec][method] = File.read(fn).split("\n").map do |line|
|
21
|
-
line.split.map{|x|
|
20
|
+
line.split.map{|x| num_class.Num(x, :base=>radix)}
|
22
21
|
end
|
23
22
|
end
|
24
23
|
end
|
24
|
+
BinNum.context = BinNum::IEEEDoubleContext
|
25
25
|
end
|
26
26
|
|
27
27
|
def check(f, radix=10, angle=:rad)
|
@@ -31,7 +31,7 @@ class TestTrig < Test::Unit::TestCase
|
|
31
31
|
class_num.context.traps[DecNum::DivisionByZero] = false
|
32
32
|
data = @data[radix][angle][prec][f]
|
33
33
|
data.each do |x, result|
|
34
|
-
assert_equal result, class_num::Math.send(f, x), "#{f}(#{x})==#{result} [#{radix} #{angle} #{prec}]"
|
34
|
+
assert_equal result, class_num::Math.send(f, x), "#{f}(#{x})==#{result}\ninput: #{x.to_int_scale.inspect} [#{radix} #{angle} #{prec}]"
|
35
35
|
end
|
36
36
|
end
|
37
37
|
end
|
@@ -43,14 +43,33 @@ class TestTrig < Test::Unit::TestCase
|
|
43
43
|
class_num.context(:precision=>prec, :angle=>angle) do
|
44
44
|
class_num.context.traps[DecNum::DivisionByZero] = false
|
45
45
|
data = @data[radix][angle][prec][f]
|
46
|
-
data.
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
46
|
+
unless data.nil?
|
47
|
+
data.each do |x, result|
|
48
|
+
y = class_num::Math.send(f, x)
|
49
|
+
if result.special?
|
50
|
+
assert_equal result, y, "#{f}(#{x})==#{result} [#{radix} #{angle} #{prec}]"
|
51
|
+
else
|
52
|
+
err_ulps = (y-result).abs/result.ulp
|
53
|
+
assert err_ulps<=ulps, "#{f}(#{x})==#{result} to within #{ulps} ulps; error: #{err_ulps} ulps (#{y})\ninput: #{x.to_int_scale.inspect} [#{radix} #{angle} #{prec}]"
|
54
|
+
end
|
53
55
|
end
|
56
|
+
else
|
57
|
+
STDERR.puts "Missing data for radix #{radix.inspect} angle #{angle.inspect} prec #{prec.inspect} #{f.inspect}"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def check_bin(f)
|
64
|
+
class_num = BinNum
|
65
|
+
@data[2][:rad].keys.each do |prec|
|
66
|
+
class_num.context do
|
67
|
+
#class_num.context.traps[DecNum::DivisionByZero] = false
|
68
|
+
data = @data[2][:rad][53][f]
|
69
|
+
data.each do |x, result|
|
70
|
+
x = class_num.Num(x)
|
71
|
+
result = ::Math.send(f, x.to_f)
|
72
|
+
assert_equal result, class_num::Math.send(f, x), "#{f}(#{x})==#{result} [bin]"
|
54
73
|
end
|
55
74
|
end
|
56
75
|
end
|
@@ -104,4 +123,32 @@ class TestTrig < Test::Unit::TestCase
|
|
104
123
|
check_relaxed :atan, 10, :deg
|
105
124
|
end
|
106
125
|
|
126
|
+
def test_sin_bin
|
127
|
+
check_relaxed :sin, 2, :rad
|
128
|
+
end
|
129
|
+
|
130
|
+
def test_cos_bin
|
131
|
+
check_relaxed :cos, 2, :rad
|
132
|
+
end
|
133
|
+
|
134
|
+
def test_tan_bin
|
135
|
+
check_relaxed :tan, 2, :rad
|
136
|
+
end
|
137
|
+
|
138
|
+
def test_asin_bin
|
139
|
+
check_relaxed :asin, 2, :rad
|
140
|
+
end
|
141
|
+
|
142
|
+
def test_acos_bin
|
143
|
+
check_relaxed :acos, 2, :rad
|
144
|
+
end
|
145
|
+
|
146
|
+
def test_atan_bin
|
147
|
+
check_relaxed :atan, 2, :rad
|
148
|
+
end
|
149
|
+
|
150
|
+
# def test_bin
|
151
|
+
# check_bin :sin
|
152
|
+
# end
|
153
|
+
|
107
154
|
end
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 1
|
7
7
|
- 2
|
8
|
-
-
|
9
|
-
version: 1.2.
|
8
|
+
- 1
|
9
|
+
version: 1.2.1
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Javier Goizueta
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-06-
|
17
|
+
date: 2010-06-16 00:00:00 +02:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|