Tolerance for floating-point types (Float, Flt::BinNum, Flt::DecNum)
Tolerance can be used to allow for a tolerance in floating-point comparisons.
A Tolerance can be defined independently of the type (floating-point numeric class) it will be used with; The actual tolerance value will be compute for a particular reference value, and for some kinds of tolerance (e.g. epsilon) a value is not available without a reference:
tol = Tolerance(3, :decimals) puts tol.value(DecNum('10.0')).inspect # -> DecNum('0.0005') puts tol.value(10.0).inspect # -> 0.0005 puts tol.value.inspect # -> Rational(1, 2000) tol = Tolerance(:epsilon) puts tol.value(DecNum('10.0')).inspect # -> DecNum('1.00E-26') puts tol.value(10.0).inspect # -> 2.22044604925031e-15 puts tol.value.inspect # -> nil
Tolerances can be:
Absolute: the tolerance value is a fixed value independent of the values to be compared.
Relative: the tolerance value is adjusted (scaled) to the magnitude of the numbers to be compared, so that it specifies admisible relative error values. Particular cases of relative tolerance are Percent and Permille tolerance.
Floating: tolerance is scaled along with the floating-point values. Floating tolerances can be :native (the scaling is done with the same base as the floating point radix), or have a specific base. Currently floating tolerances use the :low convention at the powers of the radix (as ulps). Floating tolerances should be computed at the correct or exact value to be compared, not at an approximation, but note that binary tolerance operations (equals?, less_than?, …) consider both arguments as approximations. A special case of a floating tolerance are tolerances specified in ULPs.
Tolerances can be specified as:
A specific value (valid for any type of tolerance: absolute, relative & floating)
A number of digits, or, for specific bases, decimals or bits, available for absolute and floating (significant).
Epsilon (or Big epsilon), optionally multiplied by a factor, available for all types of tolerances
A number of ULPs, which implies a floating tolerance.
A percent or permille value, only for relative tolerances.
There exists a Tolerance-derived class for each valid combination of type of tolerance and specification mode, but they all can be defined with the Tolerance() constructor. The first parameter to the constructor is the tolerance value, and in some kinds of tolerance it can be omitted. Next, the kind of tolerance is passed as a symbol; valid values are:
:absolute
:relative
:floating Generic floating decimal; another parameter can be passed for a specific base
:percent a particular kind of relative tolerance
:permille a particular kind of relative tolerance
:ulps a particular kind of floating tolerance
:sig_decimals (significative rounded decimals) a particular kind of floating tolerance; another parameter specifies if rounded
:decimals a particular kind of absolute tolerance
:sig_bits (significative bits) a particular kind of floating tolerance; another parameter specifies if rouded
:epsilon relative tolerance given as a multiple of epsilon (1 by default)
:abs_epsilon absolute tolerance given as a multiple of epsilon (1 by default)
:flt_epsilon floating tolerance given as a multiple of epsilon (1 by default)
:big_epsilon relative tolerance given as a multiple of big-epsilon (1 by default)
:abs_big_epsilon absolute tolerance given as a multiple of big-epsilon (1 by default)
:flt_big_epsilon floating tolerance given as a multiple of big-epsilon (1 by default)
Examples:
tol = Tolerance(100, :absolute) puts tol.value(1.0) # -> 100.0 puts tol.value(1.5) # -> 100.0 puts tol.value(1.0E10) # -> 100.0 puts tol.eq?(11234.0, 11280.0) # -> true tol = Tolerance(100, :relative) puts tol.value(1.0) # -> 100.0 puts tol.value(1.5) # -> 150.0 puts tol.value(1.0E10) # -> 1000000000000.0 puts tol.eq?(11234.0, 11280.0) # -> true tol = Tolerance(100, :floating) puts tol.value(1.0) # -> 100.0 puts tol.value(1.5) # -> 200.0 puts tol.value(1.0E10) # -> 1717986918400.0 puts tol.eq?(11234.0, 11280.0) # -> true tol = Tolerance(3, :sig_decimals) puts tol.eq?(1.234,1.23) # -> true tol = Tolerance(1, :ulps) puts tol.eq?(3.3433, 3.3433.next_plus) # -> true puts tol.eq?(DecNum('1.1'), DecNum('1.1').next_plus) # -> true tol = Tolerance(1, :percent) puts tol.equal_to?(3.14159, Math::PI) # -> true#