Problem with hash (again)

T

Thomas B.

It says in the documentation to hash function
(http://ruby-doc.org/core/classes/Object.html#M000337):

"Any hash value that exceeds the capacity of a Fixnum will be truncated
before being used."

From this I understand that it's OK for my object to return a Bignum.
But no:

irb(main):116:0> o=Object::new
=> #<Object:0x7f81464>
irb(main):117:0> def o.hash;89624894967898529964558;end
=> nil
irb(main):118:0> o.hash
=> 89624894967898529964558
irb(main):119:0> [o].hash
RangeError: bignum too big to convert into `long'
from (irb):119:in `hash'

My o can be used as a key in Hash but computing the hash of Array with
it inside fails. So now:
1. Isn't it inconsistent with the description? It says that hash will be
truncated before being used, so it should be truncated before being used
to calculate the hash of the array.
2. What is the walkaround? How to check if a value has already exceeded
the capacity of Fixnum, and how to truncate it manually?

It was TPR speaking, from Ruby 1.9.1p0 on Windows, thanks for listening.
 
G

Gregory Brown

1. Isn't it inconsistent with the description? It says that hash will be
truncated before being used, so it should be truncated before being used
to calculate the hash of the array.

The documentation for Object#hash says that. You're experiencing
this error with Array#hash
Not that I'm implying this is necessarily 'ok' but you had the same
mistake in your last post, reading Object's docs and applying them to
another class.
 
T

Thomas B.

Gregory said:
Not that I'm implying this is necessarily 'ok' but you had the same
mistake in your last post, reading Object's docs and applying them to
another class.

I think that if there is a description of hash or eql? or any other
method for Object and no description of this method in class X (X <
Object, of course) then the contract and general behaviour is inherited.
So it's not my mistake. If I want to learn about Float#hash and there's
no description of hash under Float then I search for the description in
Float's ancestors, until I find it, no further than under Object. And it
should apply to Floats as well.

Apart from that, if you prefer, I can call {[o]=>0}, and not directly
call Array#hash, and I get the same error of course. And now you cannot
say that I try to apply what I read about Object to Array, because now,
from the user point of view, there's simply something wrong, it doesn't
work even though I did everything OK, I redefined o.hash in a way that
is consistent with the reference for Object#hash.

For me that's a bug.
 
G

Gary Wright

2. What is the walkaround? How to check if a value has already
exceeded
the capacity of Fixnum, and how to truncate it manually?


Something like this might work...

Fixnum_max = (2**30 - 1)

def hash
compute_bignum % Fixnum_max
end

But I think it would make sense to avoid generating bignums
in the first place.

Gary Wright
 
T

Thomas Chust

2009/7/24 Thomas B. said:
It says in the documentation to hash function
(http://ruby-doc.org/core/classes/Object.html#M000337):

"Any hash value that exceeds the capacity of a Fixnum will be truncated
before being used."
[...]

Hello,

the statement you are quoting from the documentation is preceded by
the sentence "The hash value is used by class Hash.", which, in my
opinion, makes it quite clear, that the following information about
the hash value being truncated applies only to how class Hash uses the
hash value, not to the behaviour of any other class such as Array.
This also seems reasonable, since the class Object has no way of
ensuring that every caller of its hash method processes the values
returned by that method in a certain way.
[...]
2. What is the walkaround? How to check if a value has already exceeded
the capacity of Fixnum, and how to truncate it manually?
[...]

Since, according to the documentation, instances of Fixnum are signed
numbers fitting in a machine word minus one bit, and the instance
method Fixnum#size should return the size of a machine word in bytes,
you could do the following to truncate values to the fixnum range:

some_large_value & (2 ** (8 * 0.size - 2) - 1)

However, it is probably better to avoid generating numbers outside of
the fixnum range in the first place, since that will save unnecessary
computation time.

cu,
Thomas
 
G

Gregory Brown

Apart from that, if you prefer, I can call {[o]=>0}, and not directly
call Array#hash, and I get the same error of course. And now you cannot
say that I try to apply what I read about Object to Array, because now,
from the user point of view, there's simply something wrong, it doesn't
work even though I did everything OK, I redefined o.hash in a way that
is consistent with the reference for Object#hash.

And then you'd *still* be talking about Array#hash.
def o.hash; 89624894967898529964558;end => nil
a = { o => 1 } # this works!
=> {# said:
a = { [o] => 1 }
RangeError: bignum too big to convert into `long'
from (irb):23:in `hash'
from (irb):23

Just because obj.hash gets called farther down the chain isn't an
indication of how anything ought to work.

To be clear, I think it's basically a bug, for sure. But I think the
bug is that Array#hash doesn't do this truncation trick that
Object#hash claims, which doesn't tell me anything about Object#hash's
documentation.
So I'm not disagreeing with you, I'm just expressing the concern that
the issue is probably in Array, not Object.

-greg
 
G

Gregory Brown

To be clear, I think it's basically a bug, for sure. =A0 But I think the
bug is that Array#hash doesn't do this truncation trick that
Object#hash claims, which doesn't tell me anything about Object#hash's
documentation.
So I'm not disagreeing with you, I'm just expressing the concern that
the issue is probably in Array, not Object.

To further illustrate:
=3D> {#<Object:0x248d6c>=3D>1}
 
G

Gary Wright

some_large_value & (2 ** (8 * 0.size - 2) - 1)

Bitwise AND is better than my integer division
suggestion in a previous post but then again they
are probably all slow with a Bignum operand.

Gary Wright
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,982
Messages
2,570,189
Members
46,735
Latest member
HikmatRamazanov

Latest Threads

Top