diff options
-rw-r--r-- | src/backend/utils/adt/geo_ops.c | 39 | ||||
-rw-r--r-- | src/include/utils/geo_decls.h | 57 | ||||
-rw-r--r-- | src/test/regress/expected/geometry.out | 2 |
3 files changed, 72 insertions, 26 deletions
diff --git a/src/backend/utils/adt/geo_ops.c b/src/backend/utils/adt/geo_ops.c index a7db7839588..82286ef87a3 100644 --- a/src/backend/utils/adt/geo_ops.c +++ b/src/backend/utils/adt/geo_ops.c @@ -1155,9 +1155,6 @@ line_horizontal(PG_FUNCTION_ARGS) /* * Check whether the two lines are the same - * - * We consider NaNs values to be equal to each other to let those lines - * to be found. */ Datum line_eq(PG_FUNCTION_ARGS) @@ -1166,21 +1163,28 @@ line_eq(PG_FUNCTION_ARGS) LINE *l2 = PG_GETARG_LINE_P(1); float8 ratio; - if (!FPzero(l2->A) && !isnan(l2->A)) + /* If any NaNs are involved, insist on exact equality */ + if (unlikely(isnan(l1->A) || isnan(l1->B) || isnan(l1->C) || + isnan(l2->A) || isnan(l2->B) || isnan(l2->C))) + { + PG_RETURN_BOOL(float8_eq(l1->A, l2->A) && + float8_eq(l1->B, l2->B) && + float8_eq(l1->C, l2->C)); + } + + /* Otherwise, lines whose parameters are proportional are the same */ + if (!FPzero(l2->A)) ratio = float8_div(l1->A, l2->A); - else if (!FPzero(l2->B) && !isnan(l2->B)) + else if (!FPzero(l2->B)) ratio = float8_div(l1->B, l2->B); - else if (!FPzero(l2->C) && !isnan(l2->C)) + else if (!FPzero(l2->C)) ratio = float8_div(l1->C, l2->C); else ratio = 1.0; - PG_RETURN_BOOL((FPeq(l1->A, float8_mul(ratio, l2->A)) && - FPeq(l1->B, float8_mul(ratio, l2->B)) && - FPeq(l1->C, float8_mul(ratio, l2->C))) || - (float8_eq(l1->A, l2->A) && - float8_eq(l1->B, l2->B) && - float8_eq(l1->C, l2->C))); + PG_RETURN_BOOL(FPeq(l1->A, float8_mul(ratio, l2->A)) && + FPeq(l1->B, float8_mul(ratio, l2->B)) && + FPeq(l1->C, float8_mul(ratio, l2->C))); } @@ -1930,15 +1934,16 @@ point_ne(PG_FUNCTION_ARGS) /* * Check whether the two points are the same - * - * We consider NaNs coordinates to be equal to each other to let those points - * to be found. */ static inline bool point_eq_point(Point *pt1, Point *pt2) { - return ((FPeq(pt1->x, pt2->x) && FPeq(pt1->y, pt2->y)) || - (float8_eq(pt1->x, pt2->x) && float8_eq(pt1->y, pt2->y))); + /* If any NaNs are involved, insist on exact equality */ + if (unlikely(isnan(pt1->x) || isnan(pt1->y) || + isnan(pt2->x) || isnan(pt2->y))) + return (float8_eq(pt1->x, pt2->x) && float8_eq(pt1->y, pt2->y)); + + return (FPeq(pt1->x, pt2->x) && FPeq(pt1->y, pt2->y)); } diff --git a/src/include/utils/geo_decls.h b/src/include/utils/geo_decls.h index d4617558f16..1b96990dcc4 100644 --- a/src/include/utils/geo_decls.h +++ b/src/include/utils/geo_decls.h @@ -18,25 +18,66 @@ #ifndef GEO_DECLS_H #define GEO_DECLS_H +#include <math.h> + #include "fmgr.h" /*-------------------------------------------------------------------- * Useful floating point utilities and constants. - *------------------------------------------------------------------- + *-------------------------------------------------------------------- + * + * "Fuzzy" floating-point comparisons: values within EPSILON of each other + * are considered equal. Beware of normal reasoning about the behavior of + * these comparisons, since for example FPeq does not behave transitively. * - * XXX: They are not NaN-aware. + * Note that these functions are not NaN-aware and will give FALSE for + * any case involving NaN inputs. + * + * Also note that these will give sane answers for infinite inputs, + * where it's important to avoid computing Inf minus Inf; we do so + * by eliminating equality cases before subtracting. */ #define EPSILON 1.0E-06 #ifdef EPSILON #define FPzero(A) (fabs(A) <= EPSILON) -#define FPeq(A,B) (fabs((A) - (B)) <= EPSILON) -#define FPne(A,B) (fabs((A) - (B)) > EPSILON) -#define FPlt(A,B) ((B) - (A) > EPSILON) -#define FPle(A,B) ((A) - (B) <= EPSILON) -#define FPgt(A,B) ((A) - (B) > EPSILON) -#define FPge(A,B) ((B) - (A) <= EPSILON) + +static inline bool +FPeq(double A, double B) +{ + return A == B || fabs(A - B) <= EPSILON; +} + +static inline bool +FPne(double A, double B) +{ + return A != B && fabs(A - B) > EPSILON; +} + +static inline bool +FPlt(double A, double B) +{ + return A + EPSILON < B; +} + +static inline bool +FPle(double A, double B) +{ + return A <= B + EPSILON; +} + +static inline bool +FPgt(double A, double B) +{ + return A > B + EPSILON; +} + +static inline bool +FPge(double A, double B) +{ + return A + EPSILON >= B; +} #else #define FPzero(A) ((A) == 0) #define FPeq(A,B) ((A) == (B)) diff --git a/src/test/regress/expected/geometry.out b/src/test/regress/expected/geometry.out index 1ffa440a7c9..1d2508987d8 100644 --- a/src/test/regress/expected/geometry.out +++ b/src/test/regress/expected/geometry.out @@ -190,7 +190,7 @@ SELECT p1.f1, p2.f1, slope(p1.f1, p2.f1) FROM POINT_TBL p1, POINT_TBL p2; (Infinity,1e+300) | (-5,-12) | 0 (Infinity,1e+300) | (1e-300,-1e-300) | 0 (Infinity,1e+300) | (1e+300,Infinity) | NaN - (Infinity,1e+300) | (Infinity,1e+300) | 0 + (Infinity,1e+300) | (Infinity,1e+300) | 1.79769313486e+308 (Infinity,1e+300) | (NaN,NaN) | NaN (Infinity,1e+300) | (10,10) | 0 (NaN,NaN) | (0,0) | NaN |