aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/backend/access/hash/hashfunc.c21
-rw-r--r--src/test/regress/expected/hash_func.out33
-rw-r--r--src/test/regress/sql/hash_func.sql9
3 files changed, 63 insertions, 0 deletions
diff --git a/src/backend/access/hash/hashfunc.c b/src/backend/access/hash/hashfunc.c
index 6ec1ec3df3a..89d85f5a229 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"
@@ -151,6 +152,14 @@ hashfloat4(PG_FUNCTION_ARGS)
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.
* So, widen the value to float8 and hash that. (We must do this rather
@@ -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 da0948e95a9..46b9788d079 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 b7ce8b21a3a..5e4f2323868 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;