Wednesday, October 1. 2008Range bugI appear to have found a bug in ruby - specifically in ruby 1.8.4 (2005-12-24) [i686-darwin8.7.1].
This one has been baffling me for a bit so I figured it was worth posting. Given the following code: def test num You would likely expect that this method would always return true but in fact, it does not. For values of num ranging from 1 to 536870911, it returns true. For values greater than 536870911, it returns false. I thought perhaps that 536870911 is the largest possible value that can be put in a Fixnum and that larger values are converted to BigNum. That might explain a comparison failure but it turns out that a FixNum can hold 1073741823. Now it's interesting that the failure point is exactly half of the biggest value that can be put in a Fixnum but I'm not sure what conclusions we can draw from this. Trackbacks
Trackback specific URI for this entry No Trackbacks
Comments
Display comments as (Linear | Threaded)
(1..536870912).hash == (1..536870912).hash
is false That large Range seems to have a bad hash implementation. This is an artifact of Ruby's object system. Each object has an #object_id (formerly also #id), which is limited by 2**32 values (interestingly, twice the size of a fixnum). For ease of comparisons, false, true, and nil have object ids of 0, 2, and 4, respectively, while small (positive and negative) fixnums have an object id of x*2+1. So 0.object_id == 1, 1.object_id == 3, -1.object_id == -1.
As such, 536870911.object_id == 1073741823, which is a Fixnum, but 536870912.object_id == 1073741825, which is a Bignum. The confusion is that when Range#hash is called to find a bucket for it in the Hash, if the entire range is Fixnum based, it hashes consistently, but when the upper value has a Bignum object_id, it does not. Also confusing is if you hop into the land of Floats, (0..0.3).object_id is non-constant, but (0..0.3).hash is. So yeah, looks like a bug in Range#hash, also in 1.8.6. Add Comment
|