nio 0.2.1 → 0.2.2
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 +7 -0
- data/Manifest.txt +14 -15
- data/Rakefile +1 -1
- data/config/hoe.rb +14 -12
- data/config/requirements.rb +2 -2
- data/lib/nio/flttol.rb +657 -657
- data/lib/nio/fmt.rb +1917 -1883
- data/lib/nio/repdec.rb +507 -507
- data/lib/nio/rtnlzr.rb +406 -406
- data/lib/nio/tools.rb +44 -44
- data/lib/nio/version.rb +1 -1
- data/tasks/nuweb.rake +69 -69
- data/test/data.yaml +101 -101
- data/test/test_fmt.rb +410 -376
- data/test/test_helper.rb +31 -31
- data/test/test_repdec.rb +87 -87
- data/test/test_rtnlzr.rb +125 -125
- data/test/test_tools.rb +39 -39
- metadata +58 -42
- data/log/debug.log +0 -0
data/lib/nio/rtnlzr.rb
CHANGED
@@ -1,406 +1,406 @@
|
|
1
|
-
# Rationalization of floating point numbers.
|
2
|
-
#--
|
3
|
-
# Copyright (C) 2003-2005, Javier Goizueta <javier@goizueta.info>
|
4
|
-
#
|
5
|
-
# This program is free software; you can redistribute it and/or
|
6
|
-
# modify it under the terms of the GNU General Public License
|
7
|
-
# as published by the Free Software Foundation; either version 2
|
8
|
-
# of the License, or (at your option) any later version.
|
9
|
-
#++
|
10
|
-
#
|
11
|
-
# Author:: Javier Goizueta (mailto:javier@goizueta.info)
|
12
|
-
# Copyright:: Copyright (c) 2002-2004 Javier Goizueta & Joe Horn
|
13
|
-
# License:: Distributes under the GPL license
|
14
|
-
#
|
15
|
-
# This file provides conversion from floating point numbers
|
16
|
-
# to rational numbers.
|
17
|
-
# Algorithms by Joe Horn are used.
|
18
|
-
#
|
19
|
-
# The rational approximation algorithms are implemented in the class Nio::Rtnlzr
|
20
|
-
# and there's an interface to the chosen algorithms through:
|
21
|
-
# * Float#nio_r
|
22
|
-
# * BigDecimal#nio_r
|
23
|
-
# There's also exact rationalization implemented in:
|
24
|
-
# * Float#nio_xr
|
25
|
-
# * BigDecimal#nio_r
|
26
|
-
|
27
|
-
|
28
|
-
require 'nio/tools'
|
29
|
-
|
30
|
-
require 'nio/flttol'
|
31
|
-
|
32
|
-
require 'rational'
|
33
|
-
|
34
|
-
require 'bigdecimal'
|
35
|
-
|
36
|
-
|
37
|
-
class Float
|
38
|
-
# Conversion to Rational preserving the exact value of the number.
|
39
|
-
def nio_xr
|
40
|
-
return Rational(self.to_i,1) if self.modulo(1)==0
|
41
|
-
if !self.finite?
|
42
|
-
return Rational(0,0) if self.nan?
|
43
|
-
return self<0 ? Rational(-1,0) : Rational(1,0)
|
44
|
-
end
|
45
|
-
|
46
|
-
f,e = Math.frexp(self)
|
47
|
-
|
48
|
-
if e < Float::MIN_EXP
|
49
|
-
bits = e+Float::MANT_DIG-Float::MIN_EXP
|
50
|
-
else
|
51
|
-
bits = [Float::MANT_DIG,e].max
|
52
|
-
#return Rational(self.to_i,1) if bits<e
|
53
|
-
end
|
54
|
-
p = Math.ldexp(f,bits)
|
55
|
-
e = bits - e
|
56
|
-
if e<Float::MAX_EXP
|
57
|
-
q = Math.ldexp(1,e)
|
58
|
-
else
|
59
|
-
q = Float::RADIX**e
|
60
|
-
end
|
61
|
-
return Rational(p.to_i,q.to_i)
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
class BigDecimal
|
66
|
-
# Conversion to Rational preserving the exact value of the number.
|
67
|
-
def nio_xr
|
68
|
-
s,f,b,e = split
|
69
|
-
p = f.to_i
|
70
|
-
p = -p if s<0
|
71
|
-
e = f.size-e
|
72
|
-
if e<0
|
73
|
-
p *= b**(-e)
|
74
|
-
e = 0
|
75
|
-
end
|
76
|
-
q = b**(e)
|
77
|
-
return Rational(p,q)
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
class Integer
|
82
|
-
|
83
|
-
def nio_xr
|
84
|
-
return Rational(self,1)
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
class Rational
|
89
|
-
|
90
|
-
def nio_xr
|
91
|
-
return self
|
92
|
-
end
|
93
|
-
|
94
|
-
# helper method to return both the numerator and denominator
|
95
|
-
def nio_num_den
|
96
|
-
return [numerator,denominator]
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
|
101
|
-
class Float
|
102
|
-
# Conversion to Rational. The optional argument must be one of:
|
103
|
-
# - a Nio::Tolerance that defines the admisible tolerance;
|
104
|
-
# in that case, the smallest denominator rational within the
|
105
|
-
# tolerance will be found (which may take a long time for
|
106
|
-
# small tolerances.)
|
107
|
-
# - an integer that defines a maximum value for the denominator.
|
108
|
-
# in which case, the best approximation with that maximum
|
109
|
-
# denominator will be returned.
|
110
|
-
def nio_r(tol = Nio::Tolerance.big_epsilon)
|
111
|
-
case tol
|
112
|
-
when Integer
|
113
|
-
Rational(*Nio::Rtnlzr.max_denominator(self,tol,Float))
|
114
|
-
else
|
115
|
-
Rational(*Nio::Rtnlzr.new(Nio::Tol(tol)).rationalize(self))
|
116
|
-
end
|
117
|
-
end
|
118
|
-
end
|
119
|
-
|
120
|
-
class BigDecimal
|
121
|
-
# Conversion to Rational. The optional argument must be one of:
|
122
|
-
# - a Nio::BigTolerance that defines the admisible tolerance;
|
123
|
-
# in that case, the smallest denominator rational within the
|
124
|
-
# tolerance will be found (which may take a long time for
|
125
|
-
# small tolerances.)
|
126
|
-
# - an integer that defines a maximum value for the denominator.
|
127
|
-
# in which case, the best approximation with that maximum
|
128
|
-
# denominator will be returned.
|
129
|
-
def nio_r(tol = nil)
|
130
|
-
tol ||= BigTolerance.decimals([precs[0],Float::DIG].max,:sig)
|
131
|
-
case tol
|
132
|
-
when Integer
|
133
|
-
Rational(*Nio::Rtnlzr.max_denominator(self,tol,BigDecimal))
|
134
|
-
else
|
135
|
-
Rational(*Nio::Rtnlzr.new(Nio::BigTol(tol)).rationalize(self))
|
136
|
-
end
|
137
|
-
end
|
138
|
-
end
|
139
|
-
|
140
|
-
module Nio
|
141
|
-
|
142
|
-
|
143
|
-
# This class provides conversion of fractions
|
144
|
-
# (as approximate floating point numbers)
|
145
|
-
# to rational numbers.
|
146
|
-
class Rtnlzr
|
147
|
-
include StateEquivalent
|
148
|
-
|
149
|
-
# Create Rationalizator with given tolerance.
|
150
|
-
def initialize(tol=Tolerance.new)
|
151
|
-
@tol = tol
|
152
|
-
end
|
153
|
-
|
154
|
-
# Rationalization method that finds the fraction with
|
155
|
-
# smallest denominator fraction within the tolerance distance
|
156
|
-
# of an approximate (floating point) number.
|
157
|
-
#
|
158
|
-
# It uses the algorithm which has been found most efficient, rationalize_Knuth.
|
159
|
-
def rationalize(x)
|
160
|
-
rationalize_Knuth(x)
|
161
|
-
end
|
162
|
-
|
163
|
-
# This algorithm is derived from exercise 39 of 4.5.3 in
|
164
|
-
# "The Art of Computer Programming", by Donald E. Knuth.
|
165
|
-
def rationalize_Knuth(x)
|
166
|
-
|
167
|
-
|
168
|
-
num_tol = @tol.kind_of?(Numeric)
|
169
|
-
if !num_tol && @tol.zero?(x)
|
170
|
-
# num,den = x.nio_xr.nio_num_den
|
171
|
-
num,den = 0,1
|
172
|
-
else
|
173
|
-
negans=false
|
174
|
-
if x<0
|
175
|
-
negans = true
|
176
|
-
x = -x
|
177
|
-
end
|
178
|
-
dx = num_tol ? @tol : @tol.get_value(x)
|
179
|
-
|
180
|
-
|
181
|
-
x = x.nio_xr
|
182
|
-
dx = dx.nio_xr
|
183
|
-
xp,xq = (x-dx).nio_num_den
|
184
|
-
yp,yq = (x+dx).nio_num_den
|
185
|
-
|
186
|
-
a = []
|
187
|
-
fin,odd = false,false
|
188
|
-
while !fin && xp!=0 && yp!=0
|
189
|
-
odd = !odd
|
190
|
-
xp,xq = xq,xp
|
191
|
-
ax = xp.div(xq)
|
192
|
-
xp -= ax*xq
|
193
|
-
|
194
|
-
yp,yq = yq,yp
|
195
|
-
ay = yp.div(yq)
|
196
|
-
yp -= ay*yq
|
197
|
-
|
198
|
-
if ax!=ay
|
199
|
-
fin = true
|
200
|
-
ax,xp,xq = ay,yp,yq if odd
|
201
|
-
end
|
202
|
-
a << ax # .to_i
|
203
|
-
end
|
204
|
-
a[-1] += 1 if xp!=0 && a.size>0
|
205
|
-
p,q = 1,0
|
206
|
-
(1..a.size).each{|i| p,q=q+p*a[-i],p}
|
207
|
-
num,den = q,p
|
208
|
-
|
209
|
-
|
210
|
-
num = -num if negans
|
211
|
-
end
|
212
|
-
return num,den
|
213
|
-
|
214
|
-
|
215
|
-
end
|
216
|
-
# This is algorithm PDQ2 by Joe Horn.
|
217
|
-
def rationalize_Horn(x)
|
218
|
-
|
219
|
-
|
220
|
-
num_tol = @tol.kind_of?(Numeric)
|
221
|
-
if !num_tol && @tol.zero?(x)
|
222
|
-
# num,den = x.nio_xr.nio_num_den
|
223
|
-
num,den = 0,1
|
224
|
-
else
|
225
|
-
negans=false
|
226
|
-
if x<0
|
227
|
-
negans = true
|
228
|
-
x = -x
|
229
|
-
end
|
230
|
-
dx = num_tol ? @tol : @tol.get_value(x)
|
231
|
-
|
232
|
-
|
233
|
-
z,t = x,dx # renaming
|
234
|
-
|
235
|
-
a,b = t.nio_xr.nio_num_den
|
236
|
-
n0,d0 = (n,d = z.nio_xr.nio_num_den)
|
237
|
-
cn,x,pn,cd,y,pd,lo,hi,mid,q,r = 1,1,0,0,0,1,0,1,1,0,0
|
238
|
-
begin
|
239
|
-
q,r = n.divmod(d)
|
240
|
-
x = q*cn+pn
|
241
|
-
y = q*cd+pd
|
242
|
-
pn = cn
|
243
|
-
cn = x
|
244
|
-
pd = cd
|
245
|
-
cd = y
|
246
|
-
n,d = d,r
|
247
|
-
end until b*(n0*y-d0*x).abs <= a*d0*y
|
248
|
-
|
249
|
-
if q>1
|
250
|
-
hi = q
|
251
|
-
begin
|
252
|
-
mid = (lo+hi).div(2)
|
253
|
-
x = cn-pn*mid
|
254
|
-
y = cd-pd*mid
|
255
|
-
if b*(n0*y-d0*x).abs <= a*d0*y
|
256
|
-
lo = mid
|
257
|
-
else
|
258
|
-
hi = mid
|
259
|
-
end
|
260
|
-
end until hi-lo <= 1
|
261
|
-
x = cn - pn*lo
|
262
|
-
y = cd - pd*lo
|
263
|
-
end
|
264
|
-
|
265
|
-
num,den = x,y # renaming
|
266
|
-
|
267
|
-
|
268
|
-
num = -num if negans
|
269
|
-
end
|
270
|
-
return num,den
|
271
|
-
|
272
|
-
|
273
|
-
end
|
274
|
-
# This is from a RPL program by Tony Hutchins (PDR6).
|
275
|
-
def rationalize_HornHutchins(x)
|
276
|
-
|
277
|
-
|
278
|
-
num_tol = @tol.kind_of?(Numeric)
|
279
|
-
if !num_tol && @tol.zero?(x)
|
280
|
-
# num,den = x.nio_xr.nio_num_den
|
281
|
-
num,den = 0,1
|
282
|
-
else
|
283
|
-
negans=false
|
284
|
-
if x<0
|
285
|
-
negans = true
|
286
|
-
x = -x
|
287
|
-
end
|
288
|
-
dx = num_tol ? @tol : @tol.get_value(x)
|
289
|
-
|
290
|
-
|
291
|
-
z,t = x,dx # renaming
|
292
|
-
|
293
|
-
a,b = t.nio_xr.nio_num_den
|
294
|
-
n0,d0 = (n,d = z.nio_xr.nio_num_den)
|
295
|
-
cn,x,pn,cd,y,pd,lo,hi,mid,q,r = 1,1,0,0,0,1,0,1,1,0,0
|
296
|
-
begin
|
297
|
-
q,r = n.divmod(d)
|
298
|
-
x = q*cn+pn
|
299
|
-
y = q*cd+pd
|
300
|
-
pn = cn
|
301
|
-
cn = x
|
302
|
-
pd = cd
|
303
|
-
cd = y
|
304
|
-
n,d = d,r
|
305
|
-
end until b*(n0*y-d0*x).abs <= a*d0*y
|
306
|
-
|
307
|
-
if q>1
|
308
|
-
hi = q
|
309
|
-
begin
|
310
|
-
mid = (lo+hi).div(2)
|
311
|
-
x = cn-pn*mid
|
312
|
-
y = cd-pd*mid
|
313
|
-
if b*(n0*y-d0*x).abs <= a*d0*y
|
314
|
-
lo = mid
|
315
|
-
else
|
316
|
-
hi = mid
|
317
|
-
end
|
318
|
-
end until hi-lo <= 1
|
319
|
-
x = cn - pn*lo
|
320
|
-
y = cd - pd*lo
|
321
|
-
end
|
322
|
-
|
323
|
-
num,den = x,y # renaming
|
324
|
-
|
325
|
-
|
326
|
-
num = -num if negans
|
327
|
-
end
|
328
|
-
return num,den
|
329
|
-
|
330
|
-
|
331
|
-
end
|
332
|
-
end
|
333
|
-
|
334
|
-
# Best fraction given maximum denominator
|
335
|
-
# Algorithm Copyright (c) 1991 by Joseph K. Horn.
|
336
|
-
#
|
337
|
-
# The implementation of this method uses floating point
|
338
|
-
# arithmetic which limits the magnitude and precision of the results, specially
|
339
|
-
# using Float values.
|
340
|
-
def Rtnlzr.max_denominator(f, max_den=1000000000, num_class=nil)
|
341
|
-
return nil if max_den<1
|
342
|
-
num_class ||= f.class
|
343
|
-
return mth.ip(f),1 if mth.fp(f)==0
|
344
|
-
|
345
|
-
one = 1.prec(num_class)
|
346
|
-
|
347
|
-
sign = f<0
|
348
|
-
f = -f if sign
|
349
|
-
|
350
|
-
a,b,c = 0,1,f
|
351
|
-
while b<max_den and c!=0
|
352
|
-
cc = one/c
|
353
|
-
a,b,c = b, mth.ip(cc)*b+a, mth.fp(cc)
|
354
|
-
end
|
355
|
-
|
356
|
-
|
357
|
-
if b>max_den
|
358
|
-
b -= a*mth.ceil((b-max_den)/Float(a))
|
359
|
-
end
|
360
|
-
|
361
|
-
|
362
|
-
f1,f2 = [a,b].collect{|x| mth.abs(mth.rnd(x*f)/x.prec(num_class)-f)}
|
363
|
-
|
364
|
-
a = f1>f2 ? b : a
|
365
|
-
|
366
|
-
num,den = mth.rnd(a*f).to_i,a
|
367
|
-
den = 1 if mth.abs(den)<1
|
368
|
-
|
369
|
-
num = -num if sign
|
370
|
-
|
371
|
-
return num,den
|
372
|
-
end
|
373
|
-
|
374
|
-
class Rtnlzr
|
375
|
-
private
|
376
|
-
#Auxiliary floating-point functions
|
377
|
-
module Mth # :nodoc:
|
378
|
-
def self.fp(x)
|
379
|
-
# y =x.modulo(1); return x<0 ? -y : y;
|
380
|
-
x-ip(x)
|
381
|
-
end
|
382
|
-
|
383
|
-
def self.ip(x)
|
384
|
-
# x.to_i.to_f
|
385
|
-
(x<0 ? x.ceil : x.floor).to_i
|
386
|
-
end
|
387
|
-
|
388
|
-
def self.rnd(x)
|
389
|
-
#x.round.to_i
|
390
|
-
x.round
|
391
|
-
end
|
392
|
-
|
393
|
-
def self.abs(x)
|
394
|
-
x.abs
|
395
|
-
end
|
396
|
-
|
397
|
-
def self.ceil(x)
|
398
|
-
x.ceil.to_i
|
399
|
-
end
|
400
|
-
end
|
401
|
-
def self.mth; Mth; end
|
402
|
-
end
|
403
|
-
|
404
|
-
module_function
|
405
|
-
|
406
|
-
end
|
1
|
+
# Rationalization of floating point numbers.
|
2
|
+
#--
|
3
|
+
# Copyright (C) 2003-2005, Javier Goizueta <javier@goizueta.info>
|
4
|
+
#
|
5
|
+
# This program is free software; you can redistribute it and/or
|
6
|
+
# modify it under the terms of the GNU General Public License
|
7
|
+
# as published by the Free Software Foundation; either version 2
|
8
|
+
# of the License, or (at your option) any later version.
|
9
|
+
#++
|
10
|
+
#
|
11
|
+
# Author:: Javier Goizueta (mailto:javier@goizueta.info)
|
12
|
+
# Copyright:: Copyright (c) 2002-2004 Javier Goizueta & Joe Horn
|
13
|
+
# License:: Distributes under the GPL license
|
14
|
+
#
|
15
|
+
# This file provides conversion from floating point numbers
|
16
|
+
# to rational numbers.
|
17
|
+
# Algorithms by Joe Horn are used.
|
18
|
+
#
|
19
|
+
# The rational approximation algorithms are implemented in the class Nio::Rtnlzr
|
20
|
+
# and there's an interface to the chosen algorithms through:
|
21
|
+
# * Float#nio_r
|
22
|
+
# * BigDecimal#nio_r
|
23
|
+
# There's also exact rationalization implemented in:
|
24
|
+
# * Float#nio_xr
|
25
|
+
# * BigDecimal#nio_r
|
26
|
+
|
27
|
+
|
28
|
+
require 'nio/tools'
|
29
|
+
|
30
|
+
require 'nio/flttol'
|
31
|
+
|
32
|
+
require 'rational'
|
33
|
+
|
34
|
+
require 'bigdecimal'
|
35
|
+
|
36
|
+
|
37
|
+
class Float
|
38
|
+
# Conversion to Rational preserving the exact value of the number.
|
39
|
+
def nio_xr
|
40
|
+
return Rational(self.to_i,1) if self.modulo(1)==0
|
41
|
+
if !self.finite?
|
42
|
+
return Rational(0,0) if self.nan?
|
43
|
+
return self<0 ? Rational(-1,0) : Rational(1,0)
|
44
|
+
end
|
45
|
+
|
46
|
+
f,e = Math.frexp(self)
|
47
|
+
|
48
|
+
if e < Float::MIN_EXP
|
49
|
+
bits = e+Float::MANT_DIG-Float::MIN_EXP
|
50
|
+
else
|
51
|
+
bits = [Float::MANT_DIG,e].max
|
52
|
+
#return Rational(self.to_i,1) if bits<e
|
53
|
+
end
|
54
|
+
p = Math.ldexp(f,bits)
|
55
|
+
e = bits - e
|
56
|
+
if e<Float::MAX_EXP
|
57
|
+
q = Math.ldexp(1,e)
|
58
|
+
else
|
59
|
+
q = Float::RADIX**e
|
60
|
+
end
|
61
|
+
return Rational(p.to_i,q.to_i)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
class BigDecimal
|
66
|
+
# Conversion to Rational preserving the exact value of the number.
|
67
|
+
def nio_xr
|
68
|
+
s,f,b,e = split
|
69
|
+
p = f.to_i
|
70
|
+
p = -p if s<0
|
71
|
+
e = f.size-e
|
72
|
+
if e<0
|
73
|
+
p *= b**(-e)
|
74
|
+
e = 0
|
75
|
+
end
|
76
|
+
q = b**(e)
|
77
|
+
return Rational(p,q)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
class Integer
|
82
|
+
|
83
|
+
def nio_xr
|
84
|
+
return Rational(self,1)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
class Rational
|
89
|
+
|
90
|
+
def nio_xr
|
91
|
+
return self
|
92
|
+
end
|
93
|
+
|
94
|
+
# helper method to return both the numerator and denominator
|
95
|
+
def nio_num_den
|
96
|
+
return [numerator,denominator]
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
|
101
|
+
class Float
|
102
|
+
# Conversion to Rational. The optional argument must be one of:
|
103
|
+
# - a Nio::Tolerance that defines the admisible tolerance;
|
104
|
+
# in that case, the smallest denominator rational within the
|
105
|
+
# tolerance will be found (which may take a long time for
|
106
|
+
# small tolerances.)
|
107
|
+
# - an integer that defines a maximum value for the denominator.
|
108
|
+
# in which case, the best approximation with that maximum
|
109
|
+
# denominator will be returned.
|
110
|
+
def nio_r(tol = Nio::Tolerance.big_epsilon)
|
111
|
+
case tol
|
112
|
+
when Integer
|
113
|
+
Rational(*Nio::Rtnlzr.max_denominator(self,tol,Float))
|
114
|
+
else
|
115
|
+
Rational(*Nio::Rtnlzr.new(Nio::Tol(tol)).rationalize(self))
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
class BigDecimal
|
121
|
+
# Conversion to Rational. The optional argument must be one of:
|
122
|
+
# - a Nio::BigTolerance that defines the admisible tolerance;
|
123
|
+
# in that case, the smallest denominator rational within the
|
124
|
+
# tolerance will be found (which may take a long time for
|
125
|
+
# small tolerances.)
|
126
|
+
# - an integer that defines a maximum value for the denominator.
|
127
|
+
# in which case, the best approximation with that maximum
|
128
|
+
# denominator will be returned.
|
129
|
+
def nio_r(tol = nil)
|
130
|
+
tol ||= BigTolerance.decimals([precs[0],Float::DIG].max,:sig)
|
131
|
+
case tol
|
132
|
+
when Integer
|
133
|
+
Rational(*Nio::Rtnlzr.max_denominator(self,tol,BigDecimal))
|
134
|
+
else
|
135
|
+
Rational(*Nio::Rtnlzr.new(Nio::BigTol(tol)).rationalize(self))
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
module Nio
|
141
|
+
|
142
|
+
|
143
|
+
# This class provides conversion of fractions
|
144
|
+
# (as approximate floating point numbers)
|
145
|
+
# to rational numbers.
|
146
|
+
class Rtnlzr
|
147
|
+
include StateEquivalent
|
148
|
+
|
149
|
+
# Create Rationalizator with given tolerance.
|
150
|
+
def initialize(tol=Tolerance.new)
|
151
|
+
@tol = tol
|
152
|
+
end
|
153
|
+
|
154
|
+
# Rationalization method that finds the fraction with
|
155
|
+
# smallest denominator fraction within the tolerance distance
|
156
|
+
# of an approximate (floating point) number.
|
157
|
+
#
|
158
|
+
# It uses the algorithm which has been found most efficient, rationalize_Knuth.
|
159
|
+
def rationalize(x)
|
160
|
+
rationalize_Knuth(x)
|
161
|
+
end
|
162
|
+
|
163
|
+
# This algorithm is derived from exercise 39 of 4.5.3 in
|
164
|
+
# "The Art of Computer Programming", by Donald E. Knuth.
|
165
|
+
def rationalize_Knuth(x)
|
166
|
+
|
167
|
+
|
168
|
+
num_tol = @tol.kind_of?(Numeric)
|
169
|
+
if !num_tol && @tol.zero?(x)
|
170
|
+
# num,den = x.nio_xr.nio_num_den
|
171
|
+
num,den = 0,1
|
172
|
+
else
|
173
|
+
negans=false
|
174
|
+
if x<0
|
175
|
+
negans = true
|
176
|
+
x = -x
|
177
|
+
end
|
178
|
+
dx = num_tol ? @tol : @tol.get_value(x)
|
179
|
+
|
180
|
+
|
181
|
+
x = x.nio_xr
|
182
|
+
dx = dx.nio_xr
|
183
|
+
xp,xq = (x-dx).nio_num_den
|
184
|
+
yp,yq = (x+dx).nio_num_den
|
185
|
+
|
186
|
+
a = []
|
187
|
+
fin,odd = false,false
|
188
|
+
while !fin && xp!=0 && yp!=0
|
189
|
+
odd = !odd
|
190
|
+
xp,xq = xq,xp
|
191
|
+
ax = xp.div(xq)
|
192
|
+
xp -= ax*xq
|
193
|
+
|
194
|
+
yp,yq = yq,yp
|
195
|
+
ay = yp.div(yq)
|
196
|
+
yp -= ay*yq
|
197
|
+
|
198
|
+
if ax!=ay
|
199
|
+
fin = true
|
200
|
+
ax,xp,xq = ay,yp,yq if odd
|
201
|
+
end
|
202
|
+
a << ax # .to_i
|
203
|
+
end
|
204
|
+
a[-1] += 1 if xp!=0 && a.size>0
|
205
|
+
p,q = 1,0
|
206
|
+
(1..a.size).each{|i| p,q=q+p*a[-i],p}
|
207
|
+
num,den = q,p
|
208
|
+
|
209
|
+
|
210
|
+
num = -num if negans
|
211
|
+
end
|
212
|
+
return num,den
|
213
|
+
|
214
|
+
|
215
|
+
end
|
216
|
+
# This is algorithm PDQ2 by Joe Horn.
|
217
|
+
def rationalize_Horn(x)
|
218
|
+
|
219
|
+
|
220
|
+
num_tol = @tol.kind_of?(Numeric)
|
221
|
+
if !num_tol && @tol.zero?(x)
|
222
|
+
# num,den = x.nio_xr.nio_num_den
|
223
|
+
num,den = 0,1
|
224
|
+
else
|
225
|
+
negans=false
|
226
|
+
if x<0
|
227
|
+
negans = true
|
228
|
+
x = -x
|
229
|
+
end
|
230
|
+
dx = num_tol ? @tol : @tol.get_value(x)
|
231
|
+
|
232
|
+
|
233
|
+
z,t = x,dx # renaming
|
234
|
+
|
235
|
+
a,b = t.nio_xr.nio_num_den
|
236
|
+
n0,d0 = (n,d = z.nio_xr.nio_num_den)
|
237
|
+
cn,x,pn,cd,y,pd,lo,hi,mid,q,r = 1,1,0,0,0,1,0,1,1,0,0
|
238
|
+
begin
|
239
|
+
q,r = n.divmod(d)
|
240
|
+
x = q*cn+pn
|
241
|
+
y = q*cd+pd
|
242
|
+
pn = cn
|
243
|
+
cn = x
|
244
|
+
pd = cd
|
245
|
+
cd = y
|
246
|
+
n,d = d,r
|
247
|
+
end until b*(n0*y-d0*x).abs <= a*d0*y
|
248
|
+
|
249
|
+
if q>1
|
250
|
+
hi = q
|
251
|
+
begin
|
252
|
+
mid = (lo+hi).div(2)
|
253
|
+
x = cn-pn*mid
|
254
|
+
y = cd-pd*mid
|
255
|
+
if b*(n0*y-d0*x).abs <= a*d0*y
|
256
|
+
lo = mid
|
257
|
+
else
|
258
|
+
hi = mid
|
259
|
+
end
|
260
|
+
end until hi-lo <= 1
|
261
|
+
x = cn - pn*lo
|
262
|
+
y = cd - pd*lo
|
263
|
+
end
|
264
|
+
|
265
|
+
num,den = x,y # renaming
|
266
|
+
|
267
|
+
|
268
|
+
num = -num if negans
|
269
|
+
end
|
270
|
+
return num,den
|
271
|
+
|
272
|
+
|
273
|
+
end
|
274
|
+
# This is from a RPL program by Tony Hutchins (PDR6).
|
275
|
+
def rationalize_HornHutchins(x)
|
276
|
+
|
277
|
+
|
278
|
+
num_tol = @tol.kind_of?(Numeric)
|
279
|
+
if !num_tol && @tol.zero?(x)
|
280
|
+
# num,den = x.nio_xr.nio_num_den
|
281
|
+
num,den = 0,1
|
282
|
+
else
|
283
|
+
negans=false
|
284
|
+
if x<0
|
285
|
+
negans = true
|
286
|
+
x = -x
|
287
|
+
end
|
288
|
+
dx = num_tol ? @tol : @tol.get_value(x)
|
289
|
+
|
290
|
+
|
291
|
+
z,t = x,dx # renaming
|
292
|
+
|
293
|
+
a,b = t.nio_xr.nio_num_den
|
294
|
+
n0,d0 = (n,d = z.nio_xr.nio_num_den)
|
295
|
+
cn,x,pn,cd,y,pd,lo,hi,mid,q,r = 1,1,0,0,0,1,0,1,1,0,0
|
296
|
+
begin
|
297
|
+
q,r = n.divmod(d)
|
298
|
+
x = q*cn+pn
|
299
|
+
y = q*cd+pd
|
300
|
+
pn = cn
|
301
|
+
cn = x
|
302
|
+
pd = cd
|
303
|
+
cd = y
|
304
|
+
n,d = d,r
|
305
|
+
end until b*(n0*y-d0*x).abs <= a*d0*y
|
306
|
+
|
307
|
+
if q>1
|
308
|
+
hi = q
|
309
|
+
begin
|
310
|
+
mid = (lo+hi).div(2)
|
311
|
+
x = cn-pn*mid
|
312
|
+
y = cd-pd*mid
|
313
|
+
if b*(n0*y-d0*x).abs <= a*d0*y
|
314
|
+
lo = mid
|
315
|
+
else
|
316
|
+
hi = mid
|
317
|
+
end
|
318
|
+
end until hi-lo <= 1
|
319
|
+
x = cn - pn*lo
|
320
|
+
y = cd - pd*lo
|
321
|
+
end
|
322
|
+
|
323
|
+
num,den = x,y # renaming
|
324
|
+
|
325
|
+
|
326
|
+
num = -num if negans
|
327
|
+
end
|
328
|
+
return num,den
|
329
|
+
|
330
|
+
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
334
|
+
# Best fraction given maximum denominator
|
335
|
+
# Algorithm Copyright (c) 1991 by Joseph K. Horn.
|
336
|
+
#
|
337
|
+
# The implementation of this method uses floating point
|
338
|
+
# arithmetic which limits the magnitude and precision of the results, specially
|
339
|
+
# using Float values.
|
340
|
+
def Rtnlzr.max_denominator(f, max_den=1000000000, num_class=nil)
|
341
|
+
return nil if max_den<1
|
342
|
+
num_class ||= f.class
|
343
|
+
return mth.ip(f),1 if mth.fp(f)==0
|
344
|
+
|
345
|
+
one = 1.prec(num_class)
|
346
|
+
|
347
|
+
sign = f<0
|
348
|
+
f = -f if sign
|
349
|
+
|
350
|
+
a,b,c = 0,1,f
|
351
|
+
while b<max_den and c!=0
|
352
|
+
cc = one/c
|
353
|
+
a,b,c = b, mth.ip(cc)*b+a, mth.fp(cc)
|
354
|
+
end
|
355
|
+
|
356
|
+
|
357
|
+
if b>max_den
|
358
|
+
b -= a*mth.ceil((b-max_den)/Float(a))
|
359
|
+
end
|
360
|
+
|
361
|
+
|
362
|
+
f1,f2 = [a,b].collect{|x| mth.abs(mth.rnd(x*f)/x.prec(num_class)-f)}
|
363
|
+
|
364
|
+
a = f1>f2 ? b : a
|
365
|
+
|
366
|
+
num,den = mth.rnd(a*f).to_i,a
|
367
|
+
den = 1 if mth.abs(den)<1
|
368
|
+
|
369
|
+
num = -num if sign
|
370
|
+
|
371
|
+
return num,den
|
372
|
+
end
|
373
|
+
|
374
|
+
class Rtnlzr
|
375
|
+
private
|
376
|
+
#Auxiliary floating-point functions
|
377
|
+
module Mth # :nodoc:
|
378
|
+
def self.fp(x)
|
379
|
+
# y =x.modulo(1); return x<0 ? -y : y;
|
380
|
+
x-ip(x)
|
381
|
+
end
|
382
|
+
|
383
|
+
def self.ip(x)
|
384
|
+
# x.to_i.to_f
|
385
|
+
(x<0 ? x.ceil : x.floor).to_i
|
386
|
+
end
|
387
|
+
|
388
|
+
def self.rnd(x)
|
389
|
+
#x.round.to_i
|
390
|
+
x.round
|
391
|
+
end
|
392
|
+
|
393
|
+
def self.abs(x)
|
394
|
+
x.abs
|
395
|
+
end
|
396
|
+
|
397
|
+
def self.ceil(x)
|
398
|
+
x.ceil.to_i
|
399
|
+
end
|
400
|
+
end
|
401
|
+
def self.mth; Mth; end
|
402
|
+
end
|
403
|
+
|
404
|
+
module_function
|
405
|
+
|
406
|
+
end
|