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