flt 1.2.0 → 1.2.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|