flt 1.0.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.
- data/History.txt +41 -0
- data/License.txt +20 -0
- data/Manifest.txt +42 -0
- data/README.txt +557 -0
- data/Rakefile +34 -0
- data/lib/flt.rb +9 -0
- data/lib/flt/b.rb +6 -0
- data/lib/flt/bigdecimal.rb +151 -0
- data/lib/flt/bin_num.rb +250 -0
- data/lib/flt/d.rb +6 -0
- data/lib/flt/dec_num.rb +1239 -0
- data/lib/flt/float.rb +458 -0
- data/lib/flt/math.rb +66 -0
- data/lib/flt/num.rb +4211 -0
- data/lib/flt/sugar.rb +102 -0
- data/lib/flt/support.rb +1335 -0
- data/lib/flt/tolerance.rb +561 -0
- data/lib/flt/tolerance/sugar.rb +77 -0
- data/lib/flt/version.rb +9 -0
- data/setup.rb +1585 -0
- data/tasks/ann.rake +80 -0
- data/tasks/bones.rake +20 -0
- data/tasks/gem.rake +192 -0
- data/tasks/git.rake +40 -0
- data/tasks/manifest.rake +48 -0
- data/tasks/notes.rake +27 -0
- data/tasks/post_load.rake +39 -0
- data/tasks/rdoc.rake +50 -0
- data/tasks/rubyforge.rake +55 -0
- data/tasks/setup.rb +279 -0
- data/tasks/spec.rake +54 -0
- data/tasks/svn.rake +47 -0
- data/tasks/test.rake +40 -0
- data/test/all_tests.rb +23 -0
- data/test/helper.rb +101 -0
- data/test/reader.rb +68 -0
- data/test/test_basic.rb +396 -0
- data/test/test_bin.rb +245 -0
- data/test/test_bin_arithmetic.rb +94 -0
- data/test/test_binfloat_conversion.rb +24 -0
- data/test/test_coercion.rb +22 -0
- data/test/test_comparisons.rb +53 -0
- data/test/test_dectest.rb +216 -0
- data/test/test_define_conversions.rb +144 -0
- data/test/test_epsilon.rb +55 -0
- data/test/test_exact.rb +147 -0
- data/test/test_flags.rb +34 -0
- data/test/test_multithreading.rb +32 -0
- data/test/test_num_constructor.rb +133 -0
- data/test/test_odd_even.rb +78 -0
- data/test/test_round.rb +104 -0
- data/test/test_to_int.rb +104 -0
- data/test/test_to_rf.rb +36 -0
- data/test/test_tol.rb +102 -0
- data/test/test_ulp.rb +127 -0
- metadata +147 -0
@@ -0,0 +1,561 @@
|
|
1
|
+
# Tolerance for floating-point types (Float, Flt::BinNum, Flt::DecNum)
|
2
|
+
#
|
3
|
+
# Tolerance can be used to allow for a tolerance in floating-point comparisons.
|
4
|
+
#
|
5
|
+
# A Tolerance can be defined independently of the type (floating-point numeric class)
|
6
|
+
# it will be used with; The actual tolerance value will be compute for a particular reference value:
|
7
|
+
#
|
8
|
+
# tol = Tolerance(3, :decimals)
|
9
|
+
# puts tol.value(DecNum('10.0')).inspect
|
10
|
+
# puts tol.value(10.0).inspect
|
11
|
+
# puts tol.value.inspect
|
12
|
+
#
|
13
|
+
# tol = Tolerance(:epsilon)
|
14
|
+
# puts tol.value(DecNum('10.0')).inspect
|
15
|
+
# puts tol.value(10.0).inspect
|
16
|
+
# puts tol.value.inspect
|
17
|
+
#
|
18
|
+
# Tolerances can be:
|
19
|
+
# * Absolute: the tolerance value is a fixed value independent of the values to be compared.
|
20
|
+
# * Relative: the tolerance value is adjusted (scaled) to the magnitude of the numbers to be compared,
|
21
|
+
# so that it specifies admisible relative error values.
|
22
|
+
# Particular cases of relative tolerance are Percent and Permille tolerance.
|
23
|
+
# * Floating: tolerance is scaled along with the floating-point values. Floating tolerances can be
|
24
|
+
# :native (the scaling is done with the same base as the floating point radix), or have a specific base.
|
25
|
+
# Currently floating tolerances use the :low convention at the powers of the radix (as ulps). Floating
|
26
|
+
# tolerances should be computed at the correct or exact value to be compared, not at an approximation, but
|
27
|
+
# note that binary tolerance operations (equals?, less_than?, ...) consider both arguments as approximations.
|
28
|
+
# A special case of a floating tolerance are tolerances specified in ULPs.
|
29
|
+
#
|
30
|
+
# Tolerances can be specified as:
|
31
|
+
# * A specific value (valid for any type of tolerance: absolute, relative & floating)
|
32
|
+
# * A number of digits, or, for specific bases, decimals or bits, available for absolute and floating (significant).
|
33
|
+
# * Epsilon (or Big epsilon), optionally multiplied by a factor, available for all types of tolerances
|
34
|
+
# * A number of ULPs, which implies a floating tolerance.
|
35
|
+
# * A percent or permille value, only for relative tolerances.
|
36
|
+
#
|
37
|
+
# There exists a Tolerance-derived class for each valid combination of type of tolerance and specification mode,
|
38
|
+
# but they all can be defined with the Tolerance() constructor.
|
39
|
+
# The first parameter to the constructor is the tolerance value, and in some kinds of tolerance it can be
|
40
|
+
# omitted. Next, the kind of tolerance is passed as a symbol; valid values are:
|
41
|
+
# * :absolute
|
42
|
+
# * :relative
|
43
|
+
# * :floating Generic floating decimal; another parameter can be passed for a specific base
|
44
|
+
# * :percent a particular kind of relative tolerance
|
45
|
+
# * :permille a particular kind of relative tolerance
|
46
|
+
# * :ulps a particular kind of floating tolerance
|
47
|
+
# * :sig_decimals (significative rounded decimals) a particular kind of floating tolerance; another parameter specifies if rounded
|
48
|
+
# * :decimals a particular kind of absolute tolerance
|
49
|
+
# * :sig_bits (significative bits) a particular kind of floating tolerance; another parameter specifies if rouded
|
50
|
+
# * :epsilon relative tolerance given as a multiple of epsilon (1 by default)
|
51
|
+
# * :abs_epsilon absolute tolerance given as a multiple of epsilon (1 by default)
|
52
|
+
# * :flt_epsilon floating tolerance given as a multiple of epsilon (1 by default)
|
53
|
+
# * :big_epsilon relative tolerance given as a multiple of big-epsilon (1 by default)
|
54
|
+
# * :abs_big_epsilon absolute tolerance given as a multiple of big-epsilon (1 by default)
|
55
|
+
# * :flt_big_epsilon floating tolerance given as a multiple of big-epsilon (1 by default)
|
56
|
+
#
|
57
|
+
# Examples:
|
58
|
+
#
|
59
|
+
# tol = Tolerance(100, :absolute)
|
60
|
+
# puts tol.value(1.0)
|
61
|
+
# puts tol.value(1.5)
|
62
|
+
# puts tol.value(1.0E10)
|
63
|
+
# puts tol.eq?(11234.0, 11280.0)
|
64
|
+
#
|
65
|
+
# tol = Tolerance(100, :relative)
|
66
|
+
# puts tol.value(1.0)
|
67
|
+
# puts tol.value(1.5)
|
68
|
+
# puts tol.value(1.0E10)
|
69
|
+
# puts tol.eq?(11234.0, 11280.0)
|
70
|
+
#
|
71
|
+
# tol = Tolerance(100, :floating)
|
72
|
+
# puts tol.value(1.0)
|
73
|
+
# puts tol.value(1.5)
|
74
|
+
# puts tol.value(1.0E10)
|
75
|
+
# puts tol.eq?(11234.0, 11280.0)
|
76
|
+
#
|
77
|
+
# tol = Tolerance(3, :sig_decimals)
|
78
|
+
# puts tol.eq?(1.234,1.23)
|
79
|
+
#
|
80
|
+
# tol = Tolerance(1, :ulps)
|
81
|
+
# puts tol.eq?(3.3433, 3.3433.next)
|
82
|
+
# puts tol.eq?(DecNum('1.1'), DecNum('1.1').next)
|
83
|
+
#
|
84
|
+
# tol = Tolerance(1, :percent)
|
85
|
+
# puts tol.equal_to?(3.14159, Math::PI)
|
86
|
+
#
|
87
|
+
|
88
|
+
require 'flt'
|
89
|
+
require 'flt/float'
|
90
|
+
|
91
|
+
module Flt
|
92
|
+
|
93
|
+
# The Tolerance class is a base class for all tolerances.
|
94
|
+
#
|
95
|
+
# Particular tolerance *kinds* (defined by a type of tolerance and the way to specify its value) are
|
96
|
+
# implemented in separate classes derived from Tolerance.
|
97
|
+
#
|
98
|
+
# Derived classes must implement at least one of the methods relative_to() or relative_to_many()
|
99
|
+
# and may also redefine cast_value() and descr_value()
|
100
|
+
class Tolerance
|
101
|
+
|
102
|
+
def initialize(value)
|
103
|
+
@value = value
|
104
|
+
end
|
105
|
+
|
106
|
+
# Value of the tolerance for a given (floating-point) quantity
|
107
|
+
def value(x=nil)
|
108
|
+
if x
|
109
|
+
relative_to(x)
|
110
|
+
else
|
111
|
+
@value
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# Shorthand for value()
|
116
|
+
def [](x)
|
117
|
+
value(x)
|
118
|
+
end
|
119
|
+
|
120
|
+
# Description of the tolerance
|
121
|
+
def to_s
|
122
|
+
descr_value
|
123
|
+
end
|
124
|
+
|
125
|
+
# Is x nearly zero? (zero within tolerance); if a second argument y is specified:
|
126
|
+
# is x nearly zero? compared to y?
|
127
|
+
def zero?(x, y=nil)
|
128
|
+
x.zero? || x.abs < value(y || x)
|
129
|
+
end
|
130
|
+
|
131
|
+
# Returns true if the argument is approximately an integer
|
132
|
+
def integer?(x)
|
133
|
+
# Computing the tolerance at x seems the best option here
|
134
|
+
(x-x.round).abs <= relative_to(x)
|
135
|
+
end
|
136
|
+
|
137
|
+
# If the argument is close to an integer it rounds it
|
138
|
+
def integer(x)
|
139
|
+
# return integer?(x) ? x.round : nil
|
140
|
+
r = x.round
|
141
|
+
((x-r).abs <= relative_to(x)) ? r : nil
|
142
|
+
end
|
143
|
+
|
144
|
+
# Binary comparison operations that treat both arguments equally;
|
145
|
+
|
146
|
+
# less-than: x < y within tolerance
|
147
|
+
def lt?(x,y)
|
148
|
+
y-x > relative_to_many(:max, x, y)
|
149
|
+
end
|
150
|
+
|
151
|
+
# greater_than: x > y within tolerance
|
152
|
+
def gt?(x,y)
|
153
|
+
x-y > relative_to_many(:max, x, y)
|
154
|
+
end
|
155
|
+
|
156
|
+
# equals: x == y within tolerance (relaxed)
|
157
|
+
def eq?(x, y)
|
158
|
+
(x-y).abs <= relative_to_many(:max, x, y)
|
159
|
+
end
|
160
|
+
|
161
|
+
# strongly equals: x == y within tolerance (strict)
|
162
|
+
def seq?
|
163
|
+
(x-y).abs <= relative_to_many(:min, x, y)
|
164
|
+
end
|
165
|
+
|
166
|
+
# Binary operations that consider the second value the correct or exact value
|
167
|
+
|
168
|
+
# x < correct value y within tolerance
|
169
|
+
def less_than?(x,y)
|
170
|
+
y-x > relative_to(y)
|
171
|
+
end
|
172
|
+
|
173
|
+
# x > correct value y within tolerance
|
174
|
+
def greater_than?(x,y)
|
175
|
+
x-y > relative_to(y)
|
176
|
+
end
|
177
|
+
|
178
|
+
# x == correct value y within tolerance
|
179
|
+
def equal_to?(x, y)
|
180
|
+
(x-y).abs <= relative_to(y)
|
181
|
+
end
|
182
|
+
|
183
|
+
# This method is redefined in derived classes to compute the tolerance value in relation to the value x;
|
184
|
+
#
|
185
|
+
# If not redefined, relative_to_many will be used.
|
186
|
+
def relative_to(x)
|
187
|
+
relative_to_many(:max, x)
|
188
|
+
end
|
189
|
+
|
190
|
+
# This method is redefined in derived classes to compute the tolerance value in relation to the values xs;
|
191
|
+
# mode must be either :max or :min, and determines if the largerst (relaxed condition) or smallest
|
192
|
+
#(strict condition) of the relative tolerances is returned.
|
193
|
+
#
|
194
|
+
# If not redefined, relative_to will be used, but redefining this method can be used to optimize the
|
195
|
+
# performance
|
196
|
+
def relative_to_many(mode, *xs)
|
197
|
+
xs.map{|x| relative_to(x)}.send(mode)
|
198
|
+
end
|
199
|
+
|
200
|
+
# Returns the tolerance reference value for a numeric class; in derived classes this
|
201
|
+
# can be redefined to allow for values which change in value or precision depending
|
202
|
+
# on the numeric class or context.
|
203
|
+
def cast_value(num_class)
|
204
|
+
num_class.context.Num(@value)
|
205
|
+
end
|
206
|
+
|
207
|
+
# Description of the reference value (can be specialized in derived classes)
|
208
|
+
def descr_value
|
209
|
+
@value.to_s
|
210
|
+
end
|
211
|
+
|
212
|
+
# Class methods
|
213
|
+
class <<self
|
214
|
+
|
215
|
+
# Define a tolerance magnitude as a number of digits of the given base. If rounded is true
|
216
|
+
# it is assumed that results are rounded to n digits; otherwise truncation or directed rounding
|
217
|
+
# may occur and the tolerance will be larger.
|
218
|
+
def digits(base, n, rounded=true)
|
219
|
+
v = base**(-n)
|
220
|
+
v /= 2 if rounded
|
221
|
+
v
|
222
|
+
end
|
223
|
+
|
224
|
+
# Define a tolerance magnitude as a number of decimal digits. If rounded is true
|
225
|
+
# it is assumed that results are rounded to n digits; otherwise truncation or directed rounding
|
226
|
+
# may occur and the tolerance will be larger.
|
227
|
+
def decimals(n, rounded=true)
|
228
|
+
digits 10, n, rounded
|
229
|
+
end
|
230
|
+
|
231
|
+
# Define a tolerance magnitude as a number of binary digits. If rounded is true
|
232
|
+
# it is assumed that results are rounded to n digits; otherwise truncation or directed rounding
|
233
|
+
# may occur and the tolerance will be larger.
|
234
|
+
def bits(n, rounded=true)
|
235
|
+
digits 10, n, rounded
|
236
|
+
end
|
237
|
+
|
238
|
+
# Define a tolerance magnitude in relation to the 'epsilon' of the floating-point type and context.
|
239
|
+
# A multiplier may be specified to scale the epsilon.
|
240
|
+
def epsilon(num_class, mult=1)
|
241
|
+
num_class.context.epsilon*mult
|
242
|
+
end
|
243
|
+
|
244
|
+
# Define a tolerance magnitude in relation to the 'big epsilon' of the floating-point type and context.
|
245
|
+
# A multiplier may be specified to scale the big epsilon.
|
246
|
+
#
|
247
|
+
# This is a tolerance that makes multiplication associative when used with FloatingTolerance.
|
248
|
+
def big_epsilon(num_class, mult=1)
|
249
|
+
context = num_class.context
|
250
|
+
e0 = context.epsilon
|
251
|
+
# we could compute with round-up instead of using next_plus, but we can't do that with Float
|
252
|
+
den = (context.Num(1)-e0/2)
|
253
|
+
big_eps = context.next_plus(e0*2/(den*den))
|
254
|
+
big_eps*mult
|
255
|
+
end
|
256
|
+
|
257
|
+
end
|
258
|
+
|
259
|
+
end
|
260
|
+
|
261
|
+
# Implementation of absolute tolerances
|
262
|
+
class AbsoluteTolerance < Tolerance
|
263
|
+
def initialize(value)
|
264
|
+
super
|
265
|
+
end
|
266
|
+
def relative_to(x)
|
267
|
+
cast_value(x.class)
|
268
|
+
end
|
269
|
+
def relative_to_many(mode, *xs)
|
270
|
+
cast_value(xs.first.class)
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
# Implementation of relative tolerances
|
275
|
+
class RelativeTolerance < Tolerance
|
276
|
+
def initialize(value)
|
277
|
+
super
|
278
|
+
end
|
279
|
+
def relative_to(x)
|
280
|
+
x.abs*cast_value(x.class)
|
281
|
+
end
|
282
|
+
def to_s
|
283
|
+
"#{descr_value}/1"
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
# Implementation of percent (relative) tolerances
|
288
|
+
class PercentTolerance < RelativeTolerance
|
289
|
+
def initialize(value)
|
290
|
+
super
|
291
|
+
end
|
292
|
+
def to_s
|
293
|
+
"#{descr_value}%"
|
294
|
+
end
|
295
|
+
def cast_value(num_class)
|
296
|
+
num_class.Num(@value)/num_class.Num(100)
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
# Implementation of permille (relative) tolerances
|
301
|
+
class PermilleTolerance < RelativeTolerance
|
302
|
+
def initialize(value)
|
303
|
+
super
|
304
|
+
end
|
305
|
+
def to_s
|
306
|
+
"#{descr_value}/1000"
|
307
|
+
end
|
308
|
+
def cast_value(num_class)
|
309
|
+
num_class.Num(@value)/num_class.Num(1000)
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
# Implementation of floating tolerances
|
314
|
+
class FloatingTolerance < Tolerance
|
315
|
+
def initialize(value, radix=:native)
|
316
|
+
super(value)
|
317
|
+
@radix = radix
|
318
|
+
end
|
319
|
+
|
320
|
+
def to_s
|
321
|
+
if @radix==:native
|
322
|
+
"#{descr_value} flt."
|
323
|
+
else
|
324
|
+
"#{descr_value} flt.(#{radix})"
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
@float_minimum_normalized_fraction = Math.ldexp(1,-1)
|
329
|
+
def self.float_minimum_normalized_fraction
|
330
|
+
@float_minimum_normalized_fraction
|
331
|
+
end
|
332
|
+
|
333
|
+
def self.ref_adjusted_exp
|
334
|
+
-1
|
335
|
+
end
|
336
|
+
|
337
|
+
def relative_to_many(mode, *xs)
|
338
|
+
exp = nil
|
339
|
+
|
340
|
+
num_class = xs.first.class
|
341
|
+
context = num_class.context
|
342
|
+
xs = xs.map{|x| context.Num(x)}
|
343
|
+
v = cast_value(num_class)
|
344
|
+
|
345
|
+
# TODO: simplify using context
|
346
|
+
case xs.first
|
347
|
+
when Flt::Num
|
348
|
+
# TODO: handle special values
|
349
|
+
if @radix == :native || @radix == num_class.radix
|
350
|
+
exp = xs.map do |x|
|
351
|
+
x = x.normalize
|
352
|
+
exp = x.adjusted_exponent
|
353
|
+
exp -= 1 if x.coefficient == x.num_class.context.minimum_normalized_coefficient # if :low mode
|
354
|
+
exp -= FloatingTolerance.ref_adjusted_exp
|
355
|
+
exp
|
356
|
+
end.send(mode)
|
357
|
+
r = num_class.Num(+1, v.coefficient, v.exponent+exp)
|
358
|
+
r = r.normalize if num_class.radix == 2
|
359
|
+
r
|
360
|
+
elsif @radix==10
|
361
|
+
# assert x.class==BinNum
|
362
|
+
# TODO: optimize (implement log10 for BinNum)
|
363
|
+
exp = xs.map do |x|
|
364
|
+
x = x.to_decimal_exact(:exact=>true).normalize
|
365
|
+
exp = x.adjusted_exponent
|
366
|
+
exp -= 1 if x.coefficient == x.num_class.context.minimum_normalized_coefficient # if :low mode
|
367
|
+
exp -= FloatingTolerance.ref_adjusted_exp
|
368
|
+
exp
|
369
|
+
end.send(mode)
|
370
|
+
num_class.from_decimal(Flt.DecNum(+1, 1, exp)*v.to_decimal_exact)
|
371
|
+
else
|
372
|
+
# assert num_class==DecNum && @radix==2
|
373
|
+
exp = xs.map do |x|
|
374
|
+
exp = (x.ln/DecNum(2).ln).ceil.to_i - 1 # (x.ln/DecNum(2).ln).floor+1 - 1 if :high mode
|
375
|
+
exp -= FloatingTolerance.ref_adjusted_exp
|
376
|
+
exp
|
377
|
+
end.send(mode)
|
378
|
+
v*num_class.Num(2)**exp
|
379
|
+
end
|
380
|
+
when Float
|
381
|
+
if @radix == :native || @radix == Float::RADIX
|
382
|
+
exp = xs.map do |x|
|
383
|
+
f,e = Math.frexp(x)
|
384
|
+
exp = e-1
|
385
|
+
exp -= 1 if f==FloatingTolerance.float_minimum_normalized_fraction # if :low mode
|
386
|
+
exp -= FloatingTolerance.ref_adjusted_exp
|
387
|
+
end.send(mode)
|
388
|
+
Math.ldexp(v.to_f, exp)
|
389
|
+
else
|
390
|
+
# assert @radix==10
|
391
|
+
exp = xs.map do |x|
|
392
|
+
exp = Math.log10(x.abs).ceil - 1 # Math.log10(x.abs).floor+1 - 1 if :high mode
|
393
|
+
exp -= FloatingTolerance.ref_adjusted_exp
|
394
|
+
end.send(mode)
|
395
|
+
v*10.0**exp
|
396
|
+
end
|
397
|
+
when BigDecimal
|
398
|
+
if @radix == :native || @radix == 10
|
399
|
+
exp = xs.map do |x|
|
400
|
+
sign,digits,base,exp = x.split
|
401
|
+
exp -= 1
|
402
|
+
exp -= 1 if digits=="1" # if :low mode
|
403
|
+
exp -= FloatingTolerance.ref_adjusted_exp
|
404
|
+
exp
|
405
|
+
end.send(mode)
|
406
|
+
sign, digits, base, vexp = v.split
|
407
|
+
BigDecimal.new("0.#{digits}E#{vexp+exp}")
|
408
|
+
else
|
409
|
+
# assert num_class==BigDecimal && @radix==2
|
410
|
+
prec = 10
|
411
|
+
exp = xs.map do |x|
|
412
|
+
exp = (Flt::DecNum(x.to_s).ln/Flt::DecNum(2).ln).ceil - 1 # ... if :high mode
|
413
|
+
exp -= FloatingTolerance.ref_adjusted_exp
|
414
|
+
exp
|
415
|
+
end.send(mode)
|
416
|
+
context.Num(v)*context.Num(2)**exp
|
417
|
+
end
|
418
|
+
end
|
419
|
+
end
|
420
|
+
|
421
|
+
end
|
422
|
+
|
423
|
+
# Implementation of (floating) tolerances given in ULPs (units in the last place)
|
424
|
+
class UlpsTolerance < FloatingTolerance
|
425
|
+
def initialize(n=nil, num_class=nil)
|
426
|
+
@ulps = n || 1
|
427
|
+
num_class ||= Float
|
428
|
+
context = num_class.context
|
429
|
+
unit = context.Num(1)
|
430
|
+
n = context.Num(@ulps)
|
431
|
+
super(context.ulp(unit)*n)
|
432
|
+
end
|
433
|
+
def to_s
|
434
|
+
"#{@ulps} ulp#{(!@ulps.kind_of?(Numeric) || (@ulps > 1)) ? 's' : ''}"
|
435
|
+
end
|
436
|
+
def relative_to(x)
|
437
|
+
context = x.class.context
|
438
|
+
n = context.Num(@ulps)
|
439
|
+
context.ulp(x)*n
|
440
|
+
end
|
441
|
+
def relative_to_many(mode, *xs)
|
442
|
+
xs.map{|x| relative_to(x)}.send(mode)
|
443
|
+
end
|
444
|
+
end
|
445
|
+
|
446
|
+
# Implementation of (floating) tolerances given in number of significant decimal digits
|
447
|
+
class SigDecimalsTolerance < FloatingTolerance
|
448
|
+
def initialize(ndec, rounded = true)
|
449
|
+
super Tolerance.decimals(ndec, rounded), 10
|
450
|
+
@decimals = ndec
|
451
|
+
@rounded = rounded
|
452
|
+
end
|
453
|
+
def to_s
|
454
|
+
"#{@decimals} sig. #{@rounded ? 'r.' : 'r'}dec."
|
455
|
+
end
|
456
|
+
end
|
457
|
+
|
458
|
+
# Implementation of (absolute) tolerances given in number of decimal digits
|
459
|
+
class DecimalsTolerance < AbsoluteTolerance
|
460
|
+
def initialize(ndec, rounded = true)
|
461
|
+
super Tolerance.decimals(ndec, rounded)
|
462
|
+
@decimals = ndec
|
463
|
+
@rounded = rounded
|
464
|
+
end
|
465
|
+
def to_s
|
466
|
+
"#{@decimals} #{@rounded ? 'r.' : 'r'}dec."
|
467
|
+
end
|
468
|
+
end
|
469
|
+
|
470
|
+
# Implementation of (floating) tolerances given in number of significant bits
|
471
|
+
class SigBitsTolerance < FloatingTolerance
|
472
|
+
def initialize(ndec, rounded = true)
|
473
|
+
super Tolerance.bits(ndec, rounded), 2
|
474
|
+
@bits = ndec
|
475
|
+
@rounded = rounded
|
476
|
+
end
|
477
|
+
def to_s
|
478
|
+
"#{@bits} sig. #{@rounded ? 'r.' : 'r'}bits"
|
479
|
+
end
|
480
|
+
end
|
481
|
+
|
482
|
+
# Mixin for tolerances defined by Epsilon or a multiple of it
|
483
|
+
module EpsilonMixin
|
484
|
+
def initialize(mult=nil)
|
485
|
+
@mult = mult || 1
|
486
|
+
super nil
|
487
|
+
end
|
488
|
+
def cast_value(num_class)
|
489
|
+
Tolerance.epsilon(num_class, @mult)
|
490
|
+
end
|
491
|
+
def descr_value
|
492
|
+
"#{@mult==1 ? '' : "#{@mult} "} eps."
|
493
|
+
end
|
494
|
+
end
|
495
|
+
|
496
|
+
# Implementation of (relative) tolerances given as a multiple of Epsilon
|
497
|
+
class EpsilonTolerance < RelativeTolerance
|
498
|
+
include EpsilonMixin
|
499
|
+
end
|
500
|
+
|
501
|
+
# Implementation of (absolute) tolerances given as a multiple of Epsilon
|
502
|
+
class AbsEpsilonTolerance < AbsoluteTolerance
|
503
|
+
include EpsilonMixin
|
504
|
+
end
|
505
|
+
|
506
|
+
# Implementation of (floating) tolerances given as a multiple of Epsilon
|
507
|
+
class FltEpsilonTolerance < FloatingTolerance
|
508
|
+
include EpsilonMixin
|
509
|
+
end
|
510
|
+
|
511
|
+
# Mixin for tolerances defined by Big Epsilon or a multiple of it
|
512
|
+
module BigEpsilonMixin
|
513
|
+
def initialize(mult=nil)
|
514
|
+
@mult = mult || 1
|
515
|
+
super nil
|
516
|
+
end
|
517
|
+
def cast_value(num_class)
|
518
|
+
Tolerance.big_epsilon(num_class, @mult)
|
519
|
+
end
|
520
|
+
def descr_value
|
521
|
+
"#{@mult==1 ? '' : "#{@mult} "} big eps."
|
522
|
+
end
|
523
|
+
end
|
524
|
+
|
525
|
+
# Implementation of (relative) tolerances given as a multiple of Big Epsilon
|
526
|
+
class BigEpsilonTolerance < RelativeTolerance
|
527
|
+
include BigEpsilonMixin
|
528
|
+
end
|
529
|
+
|
530
|
+
# Implementation of (absolute) tolerances given as a multiple of Big Epsilon
|
531
|
+
class AbsBigEpsilonTolerance < AbsoluteTolerance
|
532
|
+
include EpsilonMixin
|
533
|
+
end
|
534
|
+
|
535
|
+
# Implementation of (floating) tolerances given as a multiple of Big Epsilon
|
536
|
+
class FltBigEpsilonTolerance < FloatingTolerance
|
537
|
+
include BigEpsilonMixin
|
538
|
+
end
|
539
|
+
|
540
|
+
module_function
|
541
|
+
# Tolerance constructor.
|
542
|
+
#
|
543
|
+
# The first parameter is the value (magnitude) of the tolerance, and is optional for some tolerances.
|
544
|
+
#
|
545
|
+
# The next parameter is the kind of tolerance as a symbol. It corresponds to the name of the
|
546
|
+
# implementation class minus the Tolerance suffix, and converted to snake-case (lowercase with underscores to
|
547
|
+
# separate words.)
|
548
|
+
#
|
549
|
+
# Finally any additional parameters admitted by the class constructor can be passed.
|
550
|
+
def Tolerance(*args)
|
551
|
+
if args.first.is_a?(Symbol)
|
552
|
+
value = nil
|
553
|
+
else
|
554
|
+
value = args.shift
|
555
|
+
end
|
556
|
+
cls_name = (args.shift || :absolute).to_s.gsub(/(^|_)(.)/){$2.upcase} + "Tolerance"
|
557
|
+
Flt.const_get(cls_name).new(value, *args)
|
558
|
+
end
|
559
|
+
|
560
|
+
end # Flt
|
561
|
+
|