[boost] hash_value( complex<T> )

4 views
Skip to first unread message

Peter Dimov via Boost

unread,
Oct 16, 2021, 6:44:44 PM10/16/21
to bo...@lists.boost.org, Peter Dimov
At the moment, boost::hash_value for complex<T> is specified as

When T is a built in type and val.imag() == 0, the result is equal
to hash_value(val.real()). Otherwise an unspecified value, except
that equal arguments shall yield the same result.

This is a bit odd; the natural implementation for a two-argument
point/tuple type is

std::size_t seed = 0;

boost::hash_combine( seed, v.real() );
boost::hash_combine( seed, v.imag() );

return seed;

Can anyone offer an opinion on whether or why the original behavior
is preferable?



_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

Howard Hinnant via Boost

unread,
Oct 16, 2021, 7:17:08 PM10/16/21
to bo...@lists.boost.org, Howard Hinnant, Peter Dimov
Given two values x and y, if x == y, it is convenient for hash(x) == hash(y).

In this situation, given a floating point value r, complex{r) == r, so it makes sense that hash(complex{r}) == hash(r). This might enable heterogeneous hash table lookup.

Howard
signature.asc

Peter Dimov via Boost

unread,
Oct 16, 2021, 7:58:27 PM10/16/21
to Howard Hinnant, bo...@lists.boost.org, Peter Dimov
Howard Hinnant wrote:
> Given two values x and y, if x == y, it is convenient for hash(x) == hash(y).
>
> In this situation, given a floating point value r, complex{r) == r, so it makes
> sense that hash(complex{r}) == hash(r). This might enable heterogeneous
> hash table lookup.

But that already doesn't hold in general for int/long, float/double,
int/double, and so on.

Howard Hinnant via Boost

unread,
Oct 16, 2021, 8:28:31 PM10/16/21
to Peter Dimov, Howard Hinnant, bo...@lists.boost.org
On Oct 16, 2021, at 7:58 PM, Peter Dimov <pdi...@gmail.com> wrote:
>
> Howard Hinnant wrote:
>> Given two values x and y, if x == y, it is convenient for hash(x) == hash(y).
>>
>> In this situation, given a floating point value r, complex{r) == r, so it makes
>> sense that hash(complex{r}) == hash(r). This might enable heterogeneous
>> hash table lookup.
>
> But that already doesn't hold in general for int/long, float/double,
> int/double, and so on.

True, but it also doesn’t not hold. :-)

So it’s a design question: should equal values hash equal, even if they are different types?

The rationale for yes is to enable heterogenous lookup.

The rationale for no is that it is not done that way for nearly all types (excepting maybe complex/real, and I’m not sure about string/string_view). There may also be performance penalties for supporting heterogeneous lookup this way because such exceptions might disable a “contiguously hashable” attribute (which isn’t really relevant for hash_combine).

So either way isn’t a slam dunk. But I’ve offered a potential advantage on "the original behavior.”

Howard

signature.asc

Andrey Semashev via Boost

unread,
Oct 17, 2021, 5:25:42 AM10/17/21
to bo...@lists.boost.org, Andrey Semashev
On 10/17/21 1:44 AM, Peter Dimov via Boost wrote:
> At the moment, boost::hash_value for complex<T> is specified as
>
> When T is a built in type and val.imag() == 0, the result is equal
> to hash_value(val.real()). Otherwise an unspecified value, except
> that equal arguments shall yield the same result.
>
> This is a bit odd; the natural implementation for a two-argument
> point/tuple type is
>
> std::size_t seed = 0;
>
> boost::hash_combine( seed, v.real() );
> boost::hash_combine( seed, v.imag() );
>
> return seed;
>
> Can anyone offer an opinion on whether or why the original behavior
> is preferable?

Not that I have a particular opinion on one way or another, but having
the hash changing the value between releases could be a breaking change,
if someone is using it e.g. in a memory mapped file. If the hash values
change as a result of your work, please be sure to mention it in the
release notes.

Peter Dimov via Boost

unread,
Oct 17, 2021, 9:54:49 AM10/17/21
to bo...@lists.boost.org, Peter Dimov
Andrey Semashev wrote:
> Not that I have a particular opinion on one way or another, but having the
> hash changing the value between releases could be a breaking change, if
> someone is using it e.g. in a memory mapped file.

Sure. As far as I can see, however, boost::hash has never promised hash
stability, and the hash values have already changed a number of times.

Peter Dimov via Boost

unread,
Oct 18, 2021, 9:48:00 PM10/18/21
to bo...@lists.boost.org, Peter Dimov
> Andrey Semashev wrote:
> > Not that I have a particular opinion on one way or another, but having the
> > hash changing the value between releases could be a breaking change, if
> > someone is using it e.g. in a memory mapped file.
>
> Sure. As far as I can see, however, boost::hash has never promised hash
> stability, and the hash values have already changed a number of times.

On second thought, you are right. While it's true that boost::hash does not
promise hash stability in principle, and that hash values have indeed changed
a number of times, the library has been relatively stable so it's possible that
people do depend on hash stability, and this should be kept in mind.

Thanks.

I'm about to add a test with reference values that will tell us if some of them
change, whether inadvertently or by design. The commit history of this test
will also be usable as a log of which release changed which hash values.

https://github.com/boostorg/container_hash/blob/feature/reference/test/hash_reference_values.cpp
Reply all
Reply to author
Forward
0 new messages