views:

79

answers:

3

I'm initializing an instance of a class that tests the equality of two formulas.

The formula's calculated values are in fact equal:

RubyChem::Chemical.new("SOOS").fw
=> 96.0

RubyChem::Chemical.new("OSSO").fw
= 96.0

When I created a new class to check the equality of these two instances I'm a bit surprised by the results:

x = RubyChem::BalanceChem.new("SOOS","OSSO")
x.balanced
=>false

y = RubyChem::BalanceChem.new("SOOS","SOOS")
y.balanced
=> true

the RubyChem::BalanceChem initialize method is here:

def initialize(formula1, formula2)
  @balanced = RubyChem::Chemical.new(formula1).fw == RubyChem::Chemical.new(formula2).fw
end

Why doesn't ruby fetch the fw values for formula1 and formula2 and check the equality of those values? What are the order of operations in Ruby and what is Ruby doing? I can see I lack an understanding of this issue. How can I make this work? Thank you in advance.

+2  A: 

This is likely another problem caused by floating point precision.

I can assure you that those values calculated before the equality is evaluated.

See the Ruby's operator precedence

NullUserException
+3  A: 

You probably do not want to check floating point numbers for equality. Instead, you should compare deltas.

Try this in irb:

x = 1.000001
y = 1.0
x == y
(x-y).abs < 0.00001

So, you find a delta like 0.00001 that you feel would handle any variation in floating point arithmetic, and use it that way. You should never == floats.

drharris
What would you use rather than floating points to compare? In chemistry the most specificity I may need is .00001; Is there any advantage to using floating points in mathematical operations?
JZ
There is no distinct advantage to using floating points instead of other types, and to be perfectly precise you would have to also take into account significant figures and handle rounding issues. However, multiplying everything by 10k and using integer math is also problematic, so I would stick with floats for now. If the most precision you need is .00001, then the best way to handle the comparison is via the method I describe above, using (x-y).abs < 0.000001 or so as your maximum delta for equality. Error is typically beyond 1e-9 range, so this would be ok.
drharris
+4  A: 

Ruby 1.8 has a bug when converting floats to string. Sometimes the given string not a good representation of the float. Here is an example with 0.56:

0.5600000000000005.to_s == 0.56.to_s  #=> true
# should have returned false, since:
0.5600000000000005      == 0.56       #=> false

This explains why two apparently identical results are not actually identical.

You probably want to do compare within a certain margin of error, do some rounding before doing a comparison, or use exact types like BigDecimal or Rational.

Marc-André Lafortune