flt 1.3.4 → 1.4.0

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.
@@ -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