Does this seem like a ruby Time equality bug? - ruby

Why the false in the 2nd comparison? I am not loading any libraries.
puts RUBY_DESCRIPTION
t = Time.now
t1 = Time.at(t.to_f)
t2 = Time.at(t.to_f)
puts( t1 == t2 )
puts( t == t1 )
puts( t.to_f == t1.to_f )
printf "%.64f\n%.64f\n%.64f\n", t, t1, t2
Output:
ruby 1.9.3p194 (2012-04-20 revision 35410) [x86_64-darwin11.4.0]
true
false
true
1347661545.4348440170288085937500000000000000000000000000000000000000000000
1347661545.4348440170288085937500000000000000000000000000000000000000000000
1347661545.4348440170288085937500000000000000000000000000000000000000000000
I get all trues on 1.8.7. What's going on?
I updated the script to show that the floats are the same, as far as I can tell. Am I missing something?

From the docs on Time.to_f: "Note that IEEE 754 double is not accurate enough to represent number of nanoseconds from the Epoch." To illustrate #oldrinb's comment:
puts RUBY_DESCRIPTION # ruby 1.9.3p194 (2012-04-20 revision 35410) [i686-linux]
t = Time.now
p t.subsec #=> (40189433/100000000); # a Rational, note the last digits 33
p t.to_f #=> 1347661635.4018943 # last digit missing 3
Time#subsec documentation: "The lowest digit of #to_f and subsec is different because IEEE 754 double is not accurate enough to represent the rational. The accurate value is returned by subsec."

I'm willing to bet that this is a classic float precision issue. Specifically, when you call #to_f, you are likely losing precision present in the original object.
You can see this easily if you compare the #nsec values of each object:
1.9.3p194 :059 > t = Time.now
=> 2012-09-14 15:29:59 -0700
1.9.3p194 :060 > t2 = Time.at(t.to_f)
=> 2012-09-14 15:29:59 -0700
1.9.3p194 :062 > t.nsec
=> 489932427
1.9.3p194 :063 > t2.nsec
=> 489932537
The reason that Time.at(t.to_f) == Time.at(t.to_f) likely succeeds is that both have the same float precision loss in their input, so their inputs are indeed identical.
So, in summary, it's buggy behavior, but it's not a bug /per se/, because it's tied to a fundamental caveat of float arithmetic.

Related

Ruby not allowing me to compare values

I made the code below, but when I run it, including if I pick the correct number, the console prints the debug. Why is this? What am I doing wrong?
puts 'Welcome to the number guessing game. I will pick a number between 1-100. It will be your job to guess the number. If you are incorrect, I will tell you if your guess is higher or lower than my number, I will let you know.'
puts "Time to guess!"
mine = (rand(1..100))
puts mine
grabber = gets.chomp!
if mine == grabber
puts 'That\'s it!'
else
print 'debug'
end
You're comparing values of two different types. gets returns a string, rand(1..100) returns an integer. You can't compare them directly. You need to convert them to the same type, either both integer or both string.
Try using to_i on the string to convert it to an integer:
if mine == grabber.to_i
As #meagar said, you are comparing different types (a string from gets and a number from rand) which will always returning false.
That being said, you have a couple of different ways to coerce/convert datatypes in ruby.
The most common one, as #maeger showed, is using to_i, however it can lead to some strange behaviours as any string that isn't easily parsed as an integer will return 0.
2.5.3 :001 > 'potato'.to_i
=> 0
2.5.3 :002 > '0xff'.to_i
=> 0
If you want to avoid this you can use Integer(arg), this is actually a method defined in Kernel that will do its best to verify if the string is actually convertible into an integer and if it fails it will raise an ArgumentError.
2.5.3 :001 > Integer('potato')
=> ArgumentError (invalid value for Integer(): 'potato')
2.5.3 :002 > Integer('2')
=> 2
2.5.3 :003 > Integer('0xff') # Hexadecimal
=> 255
2.5.3 :004 > Integer('0666') # Octal
=> 438
2.5.3 :005 > Integer('0b1110') # Binary
=> 14

Why is `9.3 == 9.3.to_d` false?

I just ran into an interesting case during TDD:
Failure/Error: expect(MoneyManager::CustomsCalculator.call(price: 31, weight: 1.12)).to eq 9.3
expected: 9.3
got: 0.93e1
I investigated further and found:
require 'bigdecimal'
=> true
2.4.2 :005 > require 'bigdecimal/util'
=> true
...
2.4.2 :008 > 1 == 1.to_d
=> true
2.4.2 :009 > 2 == 2.to_d
=> true
2.4.2 :010 > 2.0 == 2.0.to_d
=> true
2.4.2 :011 > 1.3 == 1.3.to_d
=> true
2.4.2 :012 > 9.3 == 9.3.to_d
=> false
Why is 9.3 == 9.3.to_d false?
PS, I am well aware of what a Float and a BigDecimal is, but I'm delightfully puzzled by this particular behavior.
This is not really a "ruby problem". This is a floating point representation of numbers problem.
You cannot reliably perform an equality check between floating point numbers and the "exact" value (as represented by BigDecimal).
BigDecimal.new(9.3, 2) is exact. 9.3 is not.
9.3 * 100 #=> 930.0000000000001
1.3 * 100 #=> 130.0
That's just how binary floating point numbers work. They are (sometimes) an inexact representation of the "true" value.
You can either:
Compare like-for-like (bigdecimal1 == bigdecimal2, or float1 == float2). But also note that comparing float1 == float2 is also unreliable if you're performing different calculations to get those values!! Or,
Check that the values are equal within an error bound (e.g. in rspec terms, expect(value1).to be_within(1e-12).of(value2)).
Edited due to Eric comment above
You could use the nature of float and compare it to to a limit you propose which would return either true or false reliably.
(bigdecimal-float).abs < comparison_limit
In your example that would be (I have added () to improve readability):
((9.3.to_d)-9.3).abs < 0.000001 <-- watch out for the limit!
Which yields true and can be used for testing.
Edit based on Eric's (thank you for it) comment.
It is important to always check the limits of the tolerance when comparing the two numbers.
You could do it the following way:
9.3.next_float
which would give you
9.300000000000002
so your tolerance should be
0.000000000000002
Note: watch out for the step:
9.3.next_float.next_float
=> 9.300000000000004
Now the code looks differently:
((9.3.to_d)-9.3).abs < 0.000000000000002

What is the Ruby 1.8.7 way of serializing Time without losing precision?

In Ruby 2.0 / 1.9.3
t = Time.now
t == Time.at(t.to_r)
# => true
Clearly:
t = Time.now
t == Time.at(t.to_i)
# => false
is not going to cut it.
What is the Ruby 1.8.7 way of serializing Time short of using Marshal, without losing precision?
While Time.to_r wasn't available in 1.8.7, Time.to_f was:
t = Time.now
t == Time.at(t.to_f)
# => true
Update: Since it turns out you want to do it the same way in 1.8.7 and 2.0 / 1.9.3, I'll point out that to_f is still almost certainly the best choice. While
t == Time.at(t.to_f)
now returns false, that's only because your Time object in 1.9.3 / 2.0 has even higher precision than a float.
t.to_f == Time.at(t.to_f).to_f
# => true
t - Time.at(t.to_f)
# => 8.09310302734375e-08
Those are small fractions of a microsecond being lost in conversion to float.

Float precision in ruby

I'm writing a ruby program that uses floats. I'm having trouble with the precision. For example
1.9.3p194 :013 > 113.0 * 0.01
# => 1.1300000000000001
and therefore
1.9.3p194 :018 > 113 * 0.01 == 1.13
# => false
This is exactly the sort of calculation my app needs to get right.
Is this expected? How should I go about handling this?
This is an inherent limitation in floating point numbers (even 0.01 doesn't have an exact binary floating point representation). You can use the technique provided by Aleksey or, if you want perfect precision, use the BigDecimal class bundled in ruby. It's more verbose, and slower, but it will give the right results:
require 'bigdecimal'
=> true
1.9.3p194 :003 > BigDecimal.new("113") * BigDecimal("0.01")
=> #<BigDecimal:26cefd8,'0.113E1',18(36)>
1.9.3p194 :004 > BigDecimal.new("113") * BigDecimal("0.01") == BigDecimal("1.13")
=> true
In calculation with float you should use sigma method - it means not to compare two values, but compare absolute difference of them with a very little value - 1e-10, for example.
((113 * 0.01) - 1.13).abs<1e-10

String/Range comparison problem

This make sense for things like :
irb(main):001:0> ["b", "aa", "d", "dd"].sort
=> ["aa", "b", "d", "dd"]
But doesn't for :
irb(main):002:0> ("B".."AA").each{ |x| print "#{x}," }
=> "B".."AA"
should produce :
B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,AA,=> "B".."AA" but "B" > "AA" => true
Unlike "B".."BA" ("B" > "BA" => false) :
irb(main):003:0> ("B".."BA").each{ |x| print "#{x}," }
B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,AA,AB,AC,AD,AE,AF,AG,AH,AI,AJ,AK,AL,AM,AN,AO,AP,AQ,AR,AS,AT,AU,AV,AW,AX,AY,AZ,BA,=> "B".."BA"
Any advice to make "b".."aa" work as expected in ruby ?
I use
irb 0.9.5(05/04/13) ruby 1.8.7
(2009-06-12 patchlevel 174) [i486-linux]
Linux 2.6.31-19-generic #56-Ubuntu SMP Thu Jan 28 01:26:53 UTC 2010 i686 GNU/Linux
The best way to do this is to subclass String and redefine the comparison operator to meet your needs. Then use your new class to make the range.
class MyString < String
def initialize str=""
super str
end
def <=>(other)
length_cmp = self.length <=> other.length
return length_cmp unless length_cmp == 0
super other
end
end
Now you can ensure that a column appears before another.
"b" < "aa" #=> false
MyString.new("b") < MyString.new("aa") #=> true
N.B.: Only the string on the left side of any comparison operator needs to be of class MyString:
MyString.new("b") < "aa" #=> true
"aa" > MyString.new("b") #=> false
It looks like Ruby is using succ, but first it checks for end>=start, and since that is false here, it doesn't even try.
Admittedly String#succ is a weird beast here: a string's successor isn't always greater than the string, using its own comparison methods. So I'm not sure if this is technically a bug or not. It does look pretty confusing if you don't know this undocumented check, though.
Then again, judging by some of the other answers here, it looks like it does work as you expect in some versions of Ruby, so maybe it was fixed in 1.9?
It is true that in class String, <=> and String#succ are not completely harmonized
I suppose it would be nice if for each a, b where b eventually is produced from a.succ.succ..., it was also true that a <=> b returned -1. One reason this would be nice is that it is in fact precisely <=> and succ that are used to implement ranges in the first place. Consequently, as you have noted, a Ruby String range where .succ would eventually complete the expansion doesn't work because a <=> test contradicts it and terminates the loop.
So yes, the ordering, at least for String, that is defined by <=> doesn't match the #succ method ordering. This is one reason that some developers avoid using succ.
Any advice to make "b".."aa" work as expected in ruby ?
This DOESN'T work in ruby 1.8.7 (2009-06-12 patchlevel 174) nor in ruby 1.9.1p376 (2009-12-07 revision 26041) [i486-linux]
"b".."ba" does work...
irb(main):001:0> ("b".."ba").each {|x| print "#{x} "}
b c d e f g h i j k l m n o p q r s t u v w x y z aa ab ac ad ae af ag ah ai aj ak al am an ao ap aq ar as at au av aw ax ay az ba => "b".."ba"
This was reported as a bug in Ruby here. The results depend on which version of Ruby you are running. There is a difference between versions 1.8.6 and 1.9.1.

Resources