Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 8597a48

Browse files
committed
Fix FPeq() and friends to get the right answers for infinities.
"FPeq(infinity, infinity)" returned false, on account of getting NaN when it subtracts the two inputs. Fix that by adding a separate check for exact equality. FPle() and FPge() similarly got the wrong answer for two like-signed infinities. In those cases, we can just rearrange the comparisons to avoid potentially subtracting infinities. While the sibling functions FPne() etc accidentally gave the right answers even with the internal NaN results, it seems best to make similar adjustments to them to avoid depending on this. FPeq() has to be converted to an inline function to avoid double evaluations of its arguments, and I did the same for the others just for consistency. In passing, make the handling of NaN cases in line_eq() and point_eq_point() simpler and easier to reason about, and perhaps faster. This results in just one visible regression test change: slope() now gives DBL_MAX for two inputs of (inf,1e300), which is consistent with what it does for (1e300,inf), so that seems like a bug fix. Discussion: https://postgr.es/m/CAGf+fX70rWFOk5cd00uMfa__0yP+vtQg5ck7c2Onb-Yczp0URA@mail.gmail.com
1 parent a45272b commit 8597a48

File tree

3 files changed

+72
-26
lines changed

3 files changed

+72
-26
lines changed

src/backend/utils/adt/geo_ops.c

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1155,9 +1155,6 @@ line_horizontal(PG_FUNCTION_ARGS)
11551155

11561156
/*
11571157
* Check whether the two lines are the same
1158-
*
1159-
* We consider NaNs values to be equal to each other to let those lines
1160-
* to be found.
11611158
*/
11621159
Datum
11631160
line_eq(PG_FUNCTION_ARGS)
@@ -1166,21 +1163,28 @@ line_eq(PG_FUNCTION_ARGS)
11661163
LINE *l2 = PG_GETARG_LINE_P(1);
11671164
float8 ratio;
11681165

1169-
if (!FPzero(l2->A) && !isnan(l2->A))
1166+
/* If any NaNs are involved, insist on exact equality */
1167+
if (unlikely(isnan(l1->A) || isnan(l1->B) || isnan(l1->C) ||
1168+
isnan(l2->A) || isnan(l2->B) || isnan(l2->C)))
1169+
{
1170+
PG_RETURN_BOOL(float8_eq(l1->A, l2->A) &&
1171+
float8_eq(l1->B, l2->B) &&
1172+
float8_eq(l1->C, l2->C));
1173+
}
1174+
1175+
/* Otherwise, lines whose parameters are proportional are the same */
1176+
if (!FPzero(l2->A))
11701177
ratio = float8_div(l1->A, l2->A);
1171-
else if (!FPzero(l2->B) && !isnan(l2->B))
1178+
else if (!FPzero(l2->B))
11721179
ratio = float8_div(l1->B, l2->B);
1173-
else if (!FPzero(l2->C) && !isnan(l2->C))
1180+
else if (!FPzero(l2->C))
11741181
ratio = float8_div(l1->C, l2->C);
11751182
else
11761183
ratio = 1.0;
11771184

1178-
PG_RETURN_BOOL((FPeq(l1->A, float8_mul(ratio, l2->A)) &&
1179-
FPeq(l1->B, float8_mul(ratio, l2->B)) &&
1180-
FPeq(l1->C, float8_mul(ratio, l2->C))) ||
1181-
(float8_eq(l1->A, l2->A) &&
1182-
float8_eq(l1->B, l2->B) &&
1183-
float8_eq(l1->C, l2->C)));
1185+
PG_RETURN_BOOL(FPeq(l1->A, float8_mul(ratio, l2->A)) &&
1186+
FPeq(l1->B, float8_mul(ratio, l2->B)) &&
1187+
FPeq(l1->C, float8_mul(ratio, l2->C)));
11841188
}
11851189

11861190

@@ -1930,15 +1934,16 @@ point_ne(PG_FUNCTION_ARGS)
19301934

19311935
/*
19321936
* Check whether the two points are the same
1933-
*
1934-
* We consider NaNs coordinates to be equal to each other to let those points
1935-
* to be found.
19361937
*/
19371938
static inline bool
19381939
point_eq_point(Point *pt1, Point *pt2)
19391940
{
1940-
return ((FPeq(pt1->x, pt2->x) && FPeq(pt1->y, pt2->y)) ||
1941-
(float8_eq(pt1->x, pt2->x) && float8_eq(pt1->y, pt2->y)));
1941+
/* If any NaNs are involved, insist on exact equality */
1942+
if (unlikely(isnan(pt1->x) || isnan(pt1->y) ||
1943+
isnan(pt2->x) || isnan(pt2->y)))
1944+
return (float8_eq(pt1->x, pt2->x) && float8_eq(pt1->y, pt2->y));
1945+
1946+
return (FPeq(pt1->x, pt2->x) && FPeq(pt1->y, pt2->y));
19421947
}
19431948

19441949

src/include/utils/geo_decls.h

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,25 +18,66 @@
1818
#ifndef GEO_DECLS_H
1919
#define GEO_DECLS_H
2020

21+
#include <math.h>
22+
2123
#include "fmgr.h"
2224

2325
/*--------------------------------------------------------------------
2426
* Useful floating point utilities and constants.
25-
*-------------------------------------------------------------------
27+
*--------------------------------------------------------------------
28+
*
29+
* "Fuzzy" floating-point comparisons: values within EPSILON of each other
30+
* are considered equal. Beware of normal reasoning about the behavior of
31+
* these comparisons, since for example FPeq does not behave transitively.
2632
*
27-
* XXX: They are not NaN-aware.
33+
* Note that these functions are not NaN-aware and will give FALSE for
34+
* any case involving NaN inputs.
35+
*
36+
* Also note that these will give sane answers for infinite inputs,
37+
* where it's important to avoid computing Inf minus Inf; we do so
38+
* by eliminating equality cases before subtracting.
2839
*/
2940

3041
#define EPSILON 1.0E-06
3142

3243
#ifdef EPSILON
3344
#define FPzero(A) (fabs(A) <= EPSILON)
34-
#define FPeq(A,B) (fabs((A) - (B)) <= EPSILON)
35-
#define FPne(A,B) (fabs((A) - (B)) > EPSILON)
36-
#define FPlt(A,B) ((B) - (A) > EPSILON)
37-
#define FPle(A,B) ((A) - (B) <= EPSILON)
38-
#define FPgt(A,B) ((A) - (B) > EPSILON)
39-
#define FPge(A,B) ((B) - (A) <= EPSILON)
45+
46+
static inline bool
47+
FPeq(double A, double B)
48+
{
49+
return A == B || fabs(A - B) <= EPSILON;
50+
}
51+
52+
static inline bool
53+
FPne(double A, double B)
54+
{
55+
return A != B && fabs(A - B) > EPSILON;
56+
}
57+
58+
static inline bool
59+
FPlt(double A, double B)
60+
{
61+
return A + EPSILON < B;
62+
}
63+
64+
static inline bool
65+
FPle(double A, double B)
66+
{
67+
return A <= B + EPSILON;
68+
}
69+
70+
static inline bool
71+
FPgt(double A, double B)
72+
{
73+
return A > B + EPSILON;
74+
}
75+
76+
static inline bool
77+
FPge(double A, double B)
78+
{
79+
return A + EPSILON >= B;
80+
}
4081
#else
4182
#define FPzero(A) ((A) == 0)
4283
#define FPeq(A,B) ((A) == (B))

src/test/regress/expected/geometry.out

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ SELECT p1.f1, p2.f1, slope(p1.f1, p2.f1) FROM POINT_TBL p1, POINT_TBL p2;
190190
(Infinity,1e+300) | (-5,-12) | 0
191191
(Infinity,1e+300) | (1e-300,-1e-300) | 0
192192
(Infinity,1e+300) | (1e+300,Infinity) | NaN
193-
(Infinity,1e+300) | (Infinity,1e+300) | 0
193+
(Infinity,1e+300) | (Infinity,1e+300) | 1.79769313486e+308
194194
(Infinity,1e+300) | (NaN,NaN) | NaN
195195
(Infinity,1e+300) | (10,10) | 0
196196
(NaN,NaN) | (0,0) | NaN

0 commit comments

Comments
 (0)