flt 1.3.4 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,312 @@
1
+ module Flt
2
+ module Support
3
+
4
+ # This class provides efficient conversion of fraction (as approximate floating point numbers)
5
+ # to rational numbers.
6
+ class Rationalizer
7
+ # Exact conversion to rational. Ruby provides this method for all numeric types
8
+ # since version 1.9.1, but before that it wasn't available for Float or BigDecimal.
9
+ # This methods supports old Ruby versions.
10
+ def self.to_r(x)
11
+ if x.respond_to?(:to_r)
12
+ x.to_r
13
+ else
14
+ case x
15
+ when Float
16
+ # Float did not had a #to_r method until Ruby 1.9.1
17
+ return Rational(x.to_i, 1) if x.modulo(1) == 0
18
+ if !x.finite?
19
+ return Rational(0, 0) if x.nan?
20
+ return x < 0 ? Rational(-1, 0) : Rational(1, 0)
21
+ end
22
+
23
+ f, e = Math.frexp(x)
24
+
25
+ if e < Float::MIN_EXP
26
+ bits = e + Float::MANT_DIG - Float::MIN_EXP
27
+ else
28
+ bits = [Float::MANT_DIG,e].max
29
+ # return Rational(x.to_i, 1) if bits < e
30
+ end
31
+ p = Math.ldexp(f, bits)
32
+ e = bits - e
33
+ if e < Float::MAX_EXP
34
+ q = Math.ldexp(1, e)
35
+ else
36
+ q = Float::RADIX**e
37
+ end
38
+ Rational(p.to_i, q.to_i)
39
+
40
+ when BigDecimal
41
+ # BigDecimal probably didn't have #to_r at some point
42
+ s, f, b, e = x.split
43
+ p = f.to_i
44
+ p = -p if s < 0
45
+ e = f.size - e
46
+ if e < 0
47
+ p *= b**(-e)
48
+ e = 0
49
+ end
50
+ q = b**(e)
51
+ Rational(p,q)
52
+
53
+ else
54
+ x.to_r
55
+ end
56
+ end
57
+ end
58
+
59
+ # Convenience methods
60
+ module AuxiliarFunctions
61
+ private
62
+
63
+ def num_den(x)
64
+ x = to_r(x)
65
+ [x.numerator, x.denominator]
66
+ end
67
+
68
+ # fraction part
69
+ def fp(x)
70
+ # y = x.modulo(1); return x<0 ? -y : y;
71
+ x-ip(x)
72
+ end
73
+
74
+ # integer part
75
+ def ip(x)
76
+ # Note that ceil, floor return an Integer for Float and Flt::Num, but not for BigDecimal
77
+ (x<0 ? x.ceil : x.floor).to_i
78
+ end
79
+
80
+ # round to integer
81
+ def rnd(x)
82
+ # Note that round returns an Integer for Float and Flt::Num, but not for BigDecimal
83
+ x.round.to_i
84
+ end
85
+
86
+ # absolute value
87
+ def abs(x)
88
+ x.abs
89
+ end
90
+
91
+ def ceil(x)
92
+ # Note that ceil returns an Integer for Float and Flt::Num, but not for BigDecimal
93
+ x.ceil.to_i
94
+ end
95
+
96
+ def to_r(x)
97
+ Rationalizer.to_r(x)
98
+ end
99
+
100
+ def special?(x)
101
+ !x.finite? # x.class.context.special?(x)
102
+ end
103
+
104
+ def sign(x)
105
+ x.class.context.sign(x)
106
+ end
107
+ end
108
+
109
+ include AuxiliarFunctions
110
+ extend AuxiliarFunctions
111
+
112
+ # Create Rationalizator with given tolerance.
113
+ def initialize(tol=Tolerance(:epsilon))
114
+ @tol = tol
115
+ end
116
+
117
+ def self.[](*args)
118
+ new *args
119
+ end
120
+
121
+ # Rationalization method that finds the fraction with
122
+ # smallest denominator fraction within the tolerance distance
123
+ # of an approximate (floating point) number.
124
+ #
125
+ def rationalize(x)
126
+ # Use the algorithm which has been found most efficient, rationalize_Knuth.
127
+ rationalize_Knuth(x)
128
+ end
129
+
130
+ # This algorithm is derived from exercise 39 of 4.5.3 in
131
+ # "The Art of Computer Programming", by Donald E. Knuth.
132
+ def rationalize_Knuth(x)
133
+ rationalization(x) do |x, dx|
134
+ x = to_r(x)
135
+ dx = to_r(dx)
136
+ xp,xq = num_den(x-dx)
137
+ yp,yq = num_den(x+dx)
138
+
139
+ a = []
140
+ fin, odd = false, false
141
+ while !fin && xp != 0 && yp != 0
142
+ odd = !odd
143
+ xp,xq = xq,xp
144
+ ax = xp.div(xq)
145
+ xp -= ax*xq
146
+
147
+ yp,yq = yq,yp
148
+ ay = yp.div(yq)
149
+ yp -= ay*yq
150
+
151
+ if ax!=ay
152
+ fin = true
153
+ ax,xp,xq = ay,yp,yq if odd
154
+ end
155
+ a << ax # .to_i
156
+ end
157
+ a[-1] += 1 if xp != 0 && a.size > 0
158
+ p,q = 1,0
159
+ (1..a.size).each{|i| p, q = q+p*a[-i], p}
160
+ [q, p]
161
+ end
162
+ end
163
+
164
+ # This is algorithm PDQ2 by Joe Horn.
165
+ def rationalize_Horn(x)
166
+ rationalization(x) do |z, t|
167
+ a,b = num_den(t)
168
+ n0,d0 = (n,d = num_den(z))
169
+ cn,x,pn,cd,y,pd,lo,hi,mid,q,r = 1,1,0,0,0,1,0,1,1,0,0
170
+ begin
171
+ q,r = n.divmod(d)
172
+ x = q*cn+pn
173
+ y = q*cd+pd
174
+ pn = cn
175
+ cn = x
176
+ pd = cd
177
+ cd = y
178
+ n,d = d,r
179
+ end until b*(n0*y-d0*x).abs <= a*d0*y
180
+
181
+ if q > 1
182
+ hi = q
183
+ begin
184
+ mid = (lo + hi).div(2)
185
+ x = cn - pn*mid
186
+ y = cd - pd*mid
187
+ if b*(n0*y - d0*x).abs <= a*d0*y
188
+ lo = mid
189
+ else
190
+ hi = mid
191
+ end
192
+ end until hi - lo <= 1
193
+ x = cn - pn*lo
194
+ y = cd - pd*lo
195
+ end
196
+ [x, y]
197
+ end
198
+ end
199
+
200
+ # This is from a RPL program by Tony Hutchins (PDR6).
201
+ def rationalize_HornHutchins(x)
202
+ rationalization(x) do |z, t|
203
+ a,b = num_den(t)
204
+ n0,d0 = (n,d = num_den(z))
205
+ cn,x,pn,cd,y,pd,lo,hi,mid,q,r = 1,1,0,0,0,1,0,1,1,0,0
206
+ begin
207
+ q,r = n.divmod(d)
208
+ x = q*cn+pn
209
+ y = q*cd+pd
210
+ pn = cn
211
+ cn = x
212
+ pd = cd
213
+ cd = y
214
+ n,d = d,r
215
+ end until b*(n0*y-d0*x).abs <= a*d0*y
216
+
217
+ if q > 1
218
+ hi = q
219
+ begin
220
+ mid = (lo + hi).div(2)
221
+ x = cn - pn*mid
222
+ y = cd - pd*mid
223
+ if b*(n0*y - d0*x).abs <= a*d0*y
224
+ lo = mid
225
+ else
226
+ hi = mid
227
+ end
228
+ end until hi - lo <= 1
229
+ x = cn - pn*lo
230
+ y = cd - pd*lo
231
+ end
232
+ [x, y]
233
+ end
234
+ end
235
+
236
+ # Best fraction given maximum denominator
237
+ # Algorithm Copyright (c) 1991 by Joseph K. Horn.
238
+ #
239
+ # The implementation of this method uses floating point
240
+ # arithmetic which limits the magnitude and precision of the results, specially
241
+ # using Float values.
242
+ def self.max_denominator(f, max_den=1000000000, num_class=nil)
243
+ return rationalize_special(f) if special?(f)
244
+ return nil if max_den < 1
245
+ num_class ||= f.class
246
+ context = num_class.context
247
+ return ip(f),1 if fp(f) == 0
248
+
249
+ cast = lambda{|x| context.Num(x)}
250
+
251
+ one = cast[1]
252
+
253
+ sign = f < 0
254
+ f = -f if sign
255
+
256
+ a,b,c = 0,1,f
257
+ while b < max_den && c != 0
258
+ cc = one/c
259
+ a,b,c = b, ip(cc)*b+a, fp(cc)
260
+ end
261
+
262
+ if b>max_den
263
+ b -= a*ceil(cast[b-max_den]/a)
264
+ end
265
+
266
+ f1,f2 = [a,b].collect{|x| abs(cast[rnd(x*f)]/x-f)}
267
+
268
+ a = f1 > f2 ? b : a
269
+
270
+ num,den = rnd(a*f).to_i,a
271
+ den = 1 if abs(den) < 1
272
+
273
+ num = -num if sign
274
+
275
+ return num,den
276
+ end
277
+
278
+ def self.rationalize_special(x)
279
+ if x.nan?
280
+ [0, 0]
281
+ else
282
+ [sign(x), 0]
283
+ end
284
+ end
285
+
286
+ private
287
+
288
+ def rationalization(x)
289
+ return Rationalizer.rationalize_special(x) if special?(x)
290
+ num_tol = @tol.kind_of?(Numeric)
291
+ if !num_tol && @tol.zero?(x)
292
+ # num,den = num_den(x)
293
+ num,den = 0,1
294
+ else
295
+ negans = false
296
+ if x<0
297
+ negans = true
298
+ x = -x
299
+ end
300
+ dx = num_tol ? @tol : @tol.value(x)
301
+
302
+ num, den = yield x, dx
303
+
304
+ num = -num if negans
305
+ end
306
+ [num, den]
307
+ end
308
+
309
+ end
310
+
311
+ end
312
+ end
@@ -0,0 +1,168 @@
1
+ # Some rationalization algorithms currently not being used
2
+
3
+ module Flt
4
+ module Support
5
+
6
+ class Rationalizer
7
+
8
+ # Simple Rationalization by Joe Horn
9
+ def rationalize_Horn_simple(x, smallest_denominator = false)
10
+ rationalization(x) do |z, t|
11
+ a,b = num_den(t)
12
+ n0,d0 = (n,d = z.nio_xr.nio_num_den)
13
+ cn,x,pn,cd,y,pd,lo,hi,mid,q,r = 1,1,0,0,0,1,0,1,1,0,0
14
+ begin
15
+ q,r = n.divmod(d)
16
+ x = q*cn+pn
17
+ y = q*cd+pd
18
+ pn = cn
19
+ cn = x
20
+ pd = cd
21
+ cd = y
22
+ n,d = d,r
23
+ end until b*(n0*y-d0*x).abs <= a*d0*y
24
+ if smallest_denominator
25
+ if q>1
26
+ hi = q
27
+ begin
28
+ mid = (lo+hi).div(2)
29
+ x = cn-pn*mid
30
+ y = cd-pd*mid
31
+ if b*(n0*y-d0*x).abs <= a*d0*y
32
+ lo = mid
33
+ else
34
+ hi = mid
35
+ end
36
+ end until hi-lo <= 1
37
+ x = cn - pn*lo
38
+ y = cd - pd*lo
39
+ end
40
+ end
41
+ [x, y]
42
+ end
43
+ end
44
+
45
+
46
+ # Smallest denominator rationalization procedure by Joe Horn and Tony Hutchins; this
47
+ # is the most efficient method as implemented in RPL.
48
+ # Tony Hutchins has come up with PDR6, an improvement over PDQ2;
49
+ # though benchmarking does not show any speed improvement under Ruby.
50
+ def rationalize_Horn_Hutchins(x)
51
+ rationalization(x) do |x, dx|
52
+ a,b = num_den(dx)
53
+ n,d = num_den(x)
54
+ pc,ce = n,-d
55
+ pc,cd = 1,0
56
+ t = a*b
57
+ begin
58
+ tt = (-pe).div(ce)
59
+ pd,cd = cd,pd+tt*cd
60
+ pe,ce = ce,pe+tt*ce
61
+ end until b*ce.abs <= t*cd
62
+ tt = t * (pe<0 ? -1 : (pe>0 ? +1 : 0))
63
+ tt = (tt*d+b*ce).div(tt*pd+b*pe)
64
+ [(n*cd-ce-(n*pd-pe)*tt)/d, tt/(cd-tt*pd)]
65
+ end
66
+ end
67
+
68
+ # Smallest denominator rationalization based on exercise 39 of \cite[\S 4.5.3]{Knuth}.
69
+ # This has been found the most efficient method (except for large tolerances)
70
+ # as implemented in Ruby.
71
+ # Here's the rationalization procedure based on the exercise by Knuth.
72
+ # We need first to calculate the limits (x-dx, x+dx)
73
+ # of the range where we'll look for the rational number.
74
+ # If we compute them using floating point and then convert then to fractions this method is
75
+ # always more efficient than the other procedures implemented here, but it may be
76
+ # less accurate. We can achieve perfect accuracy as the other methods by doing the
77
+ # substraction and addition with rationals, but then this method becomes less efficient than
78
+ # the others for a low number of iterations (low precision required).
79
+ def rationalize_Knuth_Goizueta(x)
80
+ rationalization(x) do |x, dx|
81
+ x = to_r(x)
82
+ dx = to_r(dx)
83
+ xp,xq = num_den(x-dx)
84
+ yp,yq = num_den(x+dx)
85
+
86
+ a = []
87
+ fin,odd = false,false
88
+ while !fin && xp!=0 && yp!=0
89
+ odd = !odd
90
+ xp,xq = xq,xp
91
+ ax = xp.div(xq)
92
+ xp -= ax*xq
93
+
94
+ yp,yq = yq,yp
95
+ ay = yp.div(yq)
96
+ yp -= ay*yq
97
+
98
+ if ax!=ay
99
+ fin = true
100
+ ax,xp,xq = ay,yp,yq if odd
101
+ end
102
+ a << ax # .to_i
103
+ end
104
+ a[-1] += 1 if xp!=0 && a.size>0
105
+ p,q = 1,0
106
+ (1..a.size).each{|i| p,q=q+p*a[-i],p}
107
+ [q, p]
108
+ end
109
+ end
110
+
111
+ # La siguiente variante realiza una iteración menos si xq<xp y una iteración más
112
+ # si xq>xp.
113
+ def rationalize_Knuth_Goizueta_b(x)
114
+ rationalization(x) do |x, dx|
115
+ x = to_r(x)
116
+ dx = to_r(dx)
117
+ xq,xp = num_den(x-dx)
118
+ yq,yp = num_den(x+dx)
119
+
120
+ a = []
121
+ fin,odd = false,false
122
+ while !fin && xp!=0 && yp!=0
123
+ odd = !odd
124
+ xp,xq = xq,xp
125
+ ax = xp.div(xq)
126
+ xp -= ax*xq
127
+
128
+ yp,yq = yq,yp
129
+ ay = yp.div(yq)
130
+ yp -= ay*yq
131
+
132
+ if ax!=ay
133
+ fin = true
134
+ ax,xp,xq = ay,yp,yq if odd
135
+ end
136
+ a << ax # .to_i
137
+ end
138
+ a[-1] += 1 if xp!=0 && a.size>0
139
+ p,q = 1,0
140
+ (1..a.size).each{|i| p,q=q+p*a[-i],p}
141
+ [p, q]
142
+ end
143
+ end
144
+
145
+ # An exact rationalization method for binary floating point
146
+ # that yields smallest fractions when possible and is not too slow
147
+ def exact_binary_rationalization(x)
148
+ p, q = x, 1
149
+ while p.modulo(1) != 0
150
+ p *= 2.0
151
+ q <<= 1 # q *= 2
152
+ end
153
+ Rational(p.to_i, q)
154
+ end
155
+
156
+ # An a here's a shorter implementation relying on the semantics of the power operator, but
157
+ # which is somewhat slow:
158
+ def exact_float_rationalization(x)
159
+ f,e = Math.frexp(x)
160
+ f = Math.ldexp(f, Float::MANT_DIG)
161
+ e -= Float::MANT_DIG
162
+ return Rational(f.to_i*(Float::RADIX**e.to_i), 1)
163
+ end
164
+
165
+ end
166
+
167
+ end
168
+ end