Class: Whitestone::Assertion::FloatEqual
- Defined in:
- lib/whitestone/assertion_classes.rb
Overview
class Assertion::KindOf
Constant Summary collapse
- EPSILON =
Experiments have shown this value to be a reliable threshold for ratio-based comparison, but that may be machine-dependant and may be overturned by more experiments. With this epsilon, floats appear to be considered equal if their first 13 significant figures are the same.
Example: 1.1 - 1.0 gives 0.10000000000000009, which differs from 0.1 in the 17th digit. Therefore, I could, and perhaps should, be more aggressive and set an even smaller ratio like 1e-16. But I assume that complicated calculations involving floats would compound representation errors, so at the moment I choose to be conservative.
It is by design that the programmer cannot specify a value for epsilon. This should “just work”, and if someone finds a case where this epsilon is not sufficient for normal use, it is probably a bug in this library that needs to be addressed. If someone wants to experiment with different epsilon values, then they can do, for example
WhiteStone::Assertion::FloatEqual.const_set :EPSILON, 1e-16
1e-13
Instance Method Summary collapse
- #floats_essentially_equal?(a, b) ⇒ Boolean
-
#initialize(mode, *args, &block) ⇒ FloatEqual
constructor
A new instance of FloatEqual.
- #message ⇒ Object
- #run ⇒ Object
Methods inherited from Base
Methods included from Guards
#args_or_block_one_only, #block_required, #no_block_allowed, #one_argument, #two_arguments, #two_or_three_arguments, #type_check
Constructor Details
#initialize(mode, *args, &block) ⇒ FloatEqual
Returns a new instance of FloatEqual.
284 285 286 287 288 289 290 |
# File 'lib/whitestone/assertion_classes.rb', line 284 def initialize(mode, *args, &block) super no_block_allowed type_check(args, Numeric) @actual, @expected = two_arguments(args).map { |x| x.to_f } @epsilon = EPSILON end |
Instance Method Details
#floats_essentially_equal?(a, b) ⇒ Boolean
308 309 310 311 |
# File 'lib/whitestone/assertion_classes.rb', line 308 def floats_essentially_equal?(a, b) @ratio = (a/b - 1).abs @ratio < EPSILON end |
#message ⇒ Object
312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 |
# File 'lib/whitestone/assertion_classes.rb', line 312 def String.new.tap { |str| case @mode when :assert str << Col["Float equality test failed"].yb str << "\n" << Col[" Should be: #{@expected.inspect}"].gb str << "\n" << Col[" Was: #{@actual.inspect}"].rb str << "\n" << " Epsilon: #{EPSILON}" if @ratio str << "\n" << " Ratio: #{@ratio}" end when :negate line = "Float inequality test failed: the two values were essentially equal." str << Col[line].yb str << "\n" << Col[" Value 1: ", @actual.inspect ].fmt(:yb, :rb) str << "\n" << Col[" Value 2: ", @expected.inspect].fmt(:yb, :rb) str << "\n" << " Epsilon: #{EPSILON}" if @ratio str << "\n" << " Ratio: #{@ratio}" end end } end |
#run ⇒ Object
291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 |
# File 'lib/whitestone/assertion_classes.rb', line 291 def run if @actual.zero? and @expected.zero? true elsif @actual.zero? or @expected.zero? # Precisely one of our values is zero. Ratios don't work in this case, # so we work around by adding 0.01 to both to get them away from zero. # We check first to be sure that this would actually work. if (@actual - @expected).abs < 0.00001 floats_essentially_equal?(@actual + 0.01, @expected + 0.01) else # They differ by more than 0.00001 so they're clearly not equal enough. false end else floats_essentially_equal?(@actual, @expected) end end |