diff --git a/src/backend/access/hash/hashfunc.c b/src/backend/access/hash/hashfunc.c index 6ec1ec3df3..89d85f5a22 100644 --- a/src/backend/access/hash/hashfunc.c +++ b/src/backend/access/hash/hashfunc.c @@ -29,6 +29,7 @@ #include "access/hash.h" #include "catalog/pg_collation.h" #include "utils/builtins.h" +#include "utils/float.h" #include "utils/hashutils.h" #include "utils/pg_locale.h" @@ -150,6 +151,14 @@ hashfloat4(PG_FUNCTION_ARGS) if (key == (float4) 0) PG_RETURN_UINT32(0); + /* + * Similarly, NaNs can have different bit patterns but they should all + * compare as equal. For backwards-compatibility reasons we force them to + * have the hash value of a standard NaN. + */ + if (isnan(key)) + key = get_float4_nan(); + /* * To support cross-type hashing of float8 and float4, we want to return * the same hash value hashfloat8 would produce for an equal float8 value. @@ -172,6 +181,8 @@ hashfloat4extended(PG_FUNCTION_ARGS) /* Same approach as hashfloat4 */ if (key == (float4) 0) PG_RETURN_UINT64(seed); + if (isnan(key)) + key = get_float4_nan(); key8 = key; return hash_any_extended((unsigned char *) &key8, sizeof(key8), seed); @@ -190,6 +201,14 @@ hashfloat8(PG_FUNCTION_ARGS) if (key == (float8) 0) PG_RETURN_UINT32(0); + /* + * Similarly, NaNs can have different bit patterns but they should all + * compare as equal. For backwards-compatibility reasons we force them to + * have the hash value of a standard NaN. + */ + if (isnan(key)) + key = get_float8_nan(); + return hash_any((unsigned char *) &key, sizeof(key)); } @@ -202,6 +221,8 @@ hashfloat8extended(PG_FUNCTION_ARGS) /* Same approach as hashfloat8 */ if (key == (float8) 0) PG_RETURN_UINT64(seed); + if (isnan(key)) + key = get_float8_nan(); return hash_any_extended((unsigned char *) &key, sizeof(key), seed); } diff --git a/src/test/regress/expected/hash_func.out b/src/test/regress/expected/hash_func.out index da0948e95a..46b9788d07 100644 --- a/src/test/regress/expected/hash_func.out +++ b/src/test/regress/expected/hash_func.out @@ -298,3 +298,36 @@ WHERE hash_range(v)::bit(32) != hash_range_extended(v, 0)::bit(32) -------+----------+-----------+----------- (0 rows) +-- +-- Check special cases for specific data types +-- +SELECT hashfloat4('0'::float4) = hashfloat4('-0'::float4) AS t; + t +--- + t +(1 row) + +SELECT hashfloat4('NaN'::float4) = hashfloat4('-NaN'::float4) AS t; + t +--- + t +(1 row) + +SELECT hashfloat8('0'::float8) = hashfloat8('-0'::float8) AS t; + t +--- + t +(1 row) + +SELECT hashfloat8('NaN'::float8) = hashfloat8('-NaN'::float8) AS t; + t +--- + t +(1 row) + +SELECT hashfloat4('NaN'::float4) = hashfloat8('NaN'::float8) AS t; + t +--- + t +(1 row) + diff --git a/src/test/regress/sql/hash_func.sql b/src/test/regress/sql/hash_func.sql index b7ce8b21a3..5e4f232386 100644 --- a/src/test/regress/sql/hash_func.sql +++ b/src/test/regress/sql/hash_func.sql @@ -220,3 +220,12 @@ FROM (VALUES (int4range(10, 20)), (int4range(23, 43)), (int4range(550274, 1550274)), (int4range(1550275, 208112489))) x(v) WHERE hash_range(v)::bit(32) != hash_range_extended(v, 0)::bit(32) OR hash_range(v)::bit(32) = hash_range_extended(v, 1)::bit(32); + +-- +-- Check special cases for specific data types +-- +SELECT hashfloat4('0'::float4) = hashfloat4('-0'::float4) AS t; +SELECT hashfloat4('NaN'::float4) = hashfloat4('-NaN'::float4) AS t; +SELECT hashfloat8('0'::float8) = hashfloat8('-0'::float8) AS t; +SELECT hashfloat8('NaN'::float8) = hashfloat8('-NaN'::float8) AS t; +SELECT hashfloat4('NaN'::float4) = hashfloat8('NaN'::float8) AS t;