aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/utils/adt/float.c437
-rw-r--r--src/include/catalog/catversion.h2
-rw-r--r--src/include/catalog/pg_proc.h18
-rw-r--r--src/include/utils/builtins.h8
-rw-r--r--src/test/regress/expected/float8-exp-three-digits-win32.out77
-rw-r--r--src/test/regress/expected/float8-small-is-zero.out77
-rw-r--r--src/test/regress/expected/float8-small-is-zero_1.out77
-rw-r--r--src/test/regress/expected/float8.out77
-rw-r--r--src/test/regress/sql/float8.sql24
9 files changed, 795 insertions, 2 deletions
diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c
index a3a989ed289..51e996ceb2a 100644
--- a/src/backend/utils/adt/float.c
+++ b/src/backend/utils/adt/float.c
@@ -1601,7 +1601,7 @@ datan(PG_FUNCTION_ARGS)
/*
- * atan2 - returns the arctan2 of arg1 (radians)
+ * atan2 - returns the arctan of arg1/arg2 (radians)
*/
Datum
datan2(PG_FUNCTION_ARGS)
@@ -1745,6 +1745,441 @@ dtan(PG_FUNCTION_ARGS)
/*
+ * asind_q1 - returns the inverse sine of x in degrees, for x in
+ * the range [0, 1]. The result is an angle in the
+ * first quadrant --- [0, 90] degrees.
+ *
+ * For the 3 special case inputs (0, 0.5 and 1), this
+ * function will return exact values (0, 30 and 90
+ * degrees respectively).
+ */
+static double
+asind_q1(double x)
+{
+ /*
+ * Stitch together inverse sine and cosine functions for the ranges [0,
+ * 0.5] and (0.5, 1]. Each expression below is guaranteed to return
+ * exactly 30 for x=0.5, so the result is a continuous monotonic function
+ * over the full range.
+ */
+ if (x <= 0.5)
+ return (asin(x) / asin(0.5)) * 30.0;
+ else
+ return 90.0 - (acos(x) / acos(0.5)) * 60.0;
+}
+
+
+/*
+ * acosd_q1 - returns the inverse cosine of x in degrees, for x in
+ * the range [0, 1]. The result is an angle in the
+ * first quadrant --- [0, 90] degrees.
+ *
+ * For the 3 special case inputs (0, 0.5 and 1), this
+ * function will return exact values (0, 60 and 90
+ * degrees respectively).
+ */
+static double
+acosd_q1(double x)
+{
+ /*
+ * Stitch together inverse sine and cosine functions for the ranges [0,
+ * 0.5] and (0.5, 1]. Each expression below is guaranteed to return
+ * exactly 60 for x=0.5, so the result is a continuous monotonic function
+ * over the full range.
+ */
+ if (x <= 0.5)
+ return 90.0 - (asin(x) / asin(0.5)) * 30.0;
+ else
+ return (acos(x) / acos(0.5)) * 60.0;
+}
+
+
+/*
+ * dacosd - returns the arccos of arg1 (degrees)
+ */
+Datum
+dacosd(PG_FUNCTION_ARGS)
+{
+ float8 arg1 = PG_GETARG_FLOAT8(0);
+ float8 result;
+
+ /* Per the POSIX spec, return NaN if the input is NaN */
+ if (isnan(arg1))
+ PG_RETURN_FLOAT8(get_float8_nan());
+
+ /*
+ * The principal branch of the inverse cosine function maps values in the
+ * range [-1, 1] to values in the range [0, 180], so we should reject any
+ * inputs outside that range and the result will always be finite.
+ */
+ if (arg1 < -1.0 || arg1 > 1.0)
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("input is out of range")));
+
+ if (arg1 >= 0.0)
+ result = acosd_q1(arg1);
+ else
+ result = 90.0 + asind_q1(-arg1);
+
+ CHECKFLOATVAL(result, false, true);
+ PG_RETURN_FLOAT8(result);
+}
+
+
+/*
+ * dasind - returns the arcsin of arg1 (degrees)
+ */
+Datum
+dasind(PG_FUNCTION_ARGS)
+{
+ float8 arg1 = PG_GETARG_FLOAT8(0);
+ float8 result;
+
+ /* Per the POSIX spec, return NaN if the input is NaN */
+ if (isnan(arg1))
+ PG_RETURN_FLOAT8(get_float8_nan());
+
+ /*
+ * The principal branch of the inverse sine function maps values in the
+ * range [-1, 1] to values in the range [-90, 90], so we should reject any
+ * inputs outside that range and the result will always be finite.
+ */
+ if (arg1 < -1.0 || arg1 > 1.0)
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("input is out of range")));
+
+ if (arg1 >= 0.0)
+ result = asind_q1(arg1);
+ else
+ result = -asind_q1(-arg1);
+
+ CHECKFLOATVAL(result, false, true);
+ PG_RETURN_FLOAT8(result);
+}
+
+
+/*
+ * datand - returns the arctan of arg1 (degrees)
+ */
+Datum
+datand(PG_FUNCTION_ARGS)
+{
+ float8 arg1 = PG_GETARG_FLOAT8(0);
+ float8 result;
+
+ /* Per the POSIX spec, return NaN if the input is NaN */
+ if (isnan(arg1))
+ PG_RETURN_FLOAT8(get_float8_nan());
+
+ /*
+ * The principal branch of the inverse tangent function maps all inputs to
+ * values in the range [-90, 90], so the result should always be finite,
+ * even if the input is infinite. Additionally, we take care to ensure
+ * than when arg1 is 1, the result is exactly 45.
+ */
+ result = (atan(arg1) / atan(1.0)) * 45.0;
+
+ CHECKFLOATVAL(result, false, true);
+ PG_RETURN_FLOAT8(result);
+}
+
+
+/*
+ * atan2d - returns the arctan of arg1/arg2 (degrees)
+ */
+Datum
+datan2d(PG_FUNCTION_ARGS)
+{
+ float8 arg1 = PG_GETARG_FLOAT8(0);
+ float8 arg2 = PG_GETARG_FLOAT8(1);
+ float8 result;
+
+ /* Per the POSIX spec, return NaN if either input is NaN */
+ if (isnan(arg1) || isnan(arg2))
+ PG_RETURN_FLOAT8(get_float8_nan());
+
+ /*
+ * atan2d maps all inputs to values in the range [-180, 180], so the
+ * result should always be finite, even if the inputs are infinite.
+ */
+ result = (atan2(arg1, arg2) / atan(1.0)) * 45.0;
+
+ CHECKFLOATVAL(result, false, true);
+ PG_RETURN_FLOAT8(result);
+}
+
+
+/*
+ * sind_0_to_30 - returns the sine of an angle that lies between 0 and
+ * 30 degrees. This will return exactly 0 when x is 0,
+ * and exactly 0.5 when x is 30 degrees.
+ */
+static double
+sind_0_to_30(double x)
+{
+ return (sin(x * (M_PI / 180.0)) / sin(30.0 * (M_PI / 180.0))) / 2.0;
+}
+
+
+/*
+ * cosd_0_to_60 - returns the cosine of an angle that lies between 0
+ * and 60 degrees. This will return exactly 1 when x
+ * is 0, and exactly 0.5 when x is 60 degrees.
+ */
+static double
+cosd_0_to_60(double x)
+{
+ return 1.0 - ((1.0 - cos(x * (M_PI / 180.0))) /
+ (1.0 - cos(60.0 * (M_PI / 180.0)))) / 2.0;
+}
+
+
+/*
+ * sind_q1 - returns the sine of an angle in the first quadrant
+ * (0 to 90 degrees).
+ */
+static double
+sind_q1(double x)
+{
+ /*
+ * Stitch together the sine and cosine functions for the ranges [0, 30]
+ * and (30, 90]. These guarantee to return exact answers at their
+ * endpoints, so the overall result is a continuous monotonic function
+ * that gives exact results when x = 0, 30 and 90 degrees.
+ */
+ if (x <= 30.0)
+ return sind_0_to_30(x);
+ else
+ return cosd_0_to_60(90.0 - x);
+}
+
+
+/*
+ * cosd_q1 - returns the cosine of an angle in the first quadrant
+ * (0 to 90 degrees).
+ */
+static double
+cosd_q1(double x)
+{
+ /*
+ * Stitch together the sine and cosine functions for the ranges [0, 60]
+ * and (60, 90]. These guarantee to return exact answers at their
+ * endpoints, so the overall result is a continuous monotonic function
+ * that gives exact results when x = 0, 60 and 90 degrees.
+ */
+ if (x <= 60.0)
+ return cosd_0_to_60(x);
+ else
+ return sind_0_to_30(90.0 - x);
+}
+
+
+/*
+ * dcosd - returns the cosine of arg1 (degrees)
+ */
+Datum
+dcosd(PG_FUNCTION_ARGS)
+{
+ float8 arg1 = PG_GETARG_FLOAT8(0);
+ int sign = 1;
+ float8 result;
+
+ /*
+ * Per the POSIX spec, return NaN if the input is NaN and throw an error
+ * if the input is infinite.
+ */
+ if (isnan(arg1))
+ PG_RETURN_FLOAT8(get_float8_nan());
+
+ if (isinf(arg1))
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("input is out of range")));
+
+ /* Reduce the range of the input to [0,90] degrees */
+ arg1 = fmod(arg1, 360.0);
+
+ if (arg1 < 0.0)
+ /* cosd(-x) = cosd(x) */
+ arg1 = -arg1;
+
+ if (arg1 > 180.0)
+ /* cosd(360-x) = cosd(x) */
+ arg1 = 360.0 - arg1;
+
+ if (arg1 > 90.0)
+ {
+ /* cosd(180-x) = -cosd(x) */
+ arg1 = 180.0 - arg1;
+ sign = -sign;
+ }
+
+ result = sign * cosd_q1(arg1);
+
+ CHECKFLOATVAL(result, false, true);
+ PG_RETURN_FLOAT8(result);
+}
+
+
+/*
+ * dcotd - returns the cotangent of arg1 (degrees)
+ */
+Datum
+dcotd(PG_FUNCTION_ARGS)
+{
+ float8 arg1 = PG_GETARG_FLOAT8(0);
+ int sign = 1;
+ float8 result;
+
+ /*
+ * Per the POSIX spec, return NaN if the input is NaN and throw an error
+ * if the input is infinite.
+ */
+ if (isnan(arg1))
+ PG_RETURN_FLOAT8(get_float8_nan());
+
+ if (isinf(arg1))
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("input is out of range")));
+
+ /* Reduce the range of the input to [0,90] degrees */
+ arg1 = fmod(arg1, 360.0);
+
+ if (arg1 < 0.0)
+ {
+ /* cotd(-x) = -cotd(x) */
+ arg1 = -arg1;
+ sign = -sign;
+ }
+
+ if (arg1 > 180.0)
+ {
+ /* cotd(360-x) = -cotd(x) */
+ arg1 = 360.0 - arg1;
+ sign = -sign;
+ }
+
+ if (arg1 > 90.0)
+ {
+ /* cotd(180-x) = -cotd(x) */
+ arg1 = 180.0 - arg1;
+ sign = -sign;
+ }
+
+ result = sign * cosd_q1(arg1) / sind_q1(arg1);
+
+ CHECKFLOATVAL(result, true /* cotd(0) == Inf */ , true);
+ PG_RETURN_FLOAT8(result);
+}
+
+
+/*
+ * dsind - returns the sine of arg1 (degrees)
+ */
+Datum
+dsind(PG_FUNCTION_ARGS)
+{
+ float8 arg1 = PG_GETARG_FLOAT8(0);
+ int sign = 1;
+ float8 result;
+
+ /*
+ * Per the POSIX spec, return NaN if the input is NaN and throw an error
+ * if the input is infinite.
+ */
+ if (isnan(arg1))
+ PG_RETURN_FLOAT8(get_float8_nan());
+
+ if (isinf(arg1))
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("input is out of range")));
+
+ /* Reduce the range of the input to [0,90] degrees */
+ arg1 = fmod(arg1, 360.0);
+
+ if (arg1 < 0.0)
+ {
+ /* sind(-x) = -sind(x) */
+ arg1 = -arg1;
+ sign = -sign;
+ }
+
+ if (arg1 > 180.0)
+ {
+ /* sind(360-x) = -sind(x) */
+ arg1 = 360.0 - arg1;
+ sign = -sign;
+ }
+
+ if (arg1 > 90.0)
+ /* sind(180-x) = sind(x) */
+ arg1 = 180.0 - arg1;
+
+ result = sign * sind_q1(arg1);
+
+ CHECKFLOATVAL(result, false, true);
+ PG_RETURN_FLOAT8(result);
+}
+
+
+/*
+ * dtand - returns the tangent of arg1 (degrees)
+ */
+Datum
+dtand(PG_FUNCTION_ARGS)
+{
+ float8 arg1 = PG_GETARG_FLOAT8(0);
+ int sign = 1;
+ float8 result;
+
+ /*
+ * Per the POSIX spec, return NaN if the input is NaN and throw an error
+ * if the input is infinite.
+ */
+ if (isnan(arg1))
+ PG_RETURN_FLOAT8(get_float8_nan());
+
+ if (isinf(arg1))
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("input is out of range")));
+
+ /* Reduce the range of the input to [0,90] degrees */
+ arg1 = fmod(arg1, 360.0);
+
+ if (arg1 < 0.0)
+ {
+ /* tand(-x) = -tand(x) */
+ arg1 = -arg1;
+ sign = -sign;
+ }
+
+ if (arg1 > 180.0)
+ {
+ /* tand(360-x) = -tand(x) */
+ arg1 = 360.0 - arg1;
+ sign = -sign;
+ }
+
+ if (arg1 > 90.0)
+ {
+ /* tand(180-x) = -tand(x) */
+ arg1 = 180.0 - arg1;
+ sign = -sign;
+ }
+
+ result = sign * sind_q1(arg1) / cosd_q1(arg1);
+
+ CHECKFLOATVAL(result, true /* tand(90) == Inf */ , true);
+ PG_RETURN_FLOAT8(result);
+}
+
+
+/*
* degrees - returns degrees converted from radians
*/
Datum
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 58709f56ac5..894a619f94b 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 201601201
+#define CATALOG_VERSION_NO 201601221
#endif
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 244aa4d016a..79e92ffa785 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -1793,6 +1793,24 @@ DATA(insert OID = 1606 ( tan PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 701
DESCR("tangent");
DATA(insert OID = 1607 ( cot PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 701 "701" _null_ _null_ _null_ _null_ _null_ dcot _null_ _null_ _null_ ));
DESCR("cotangent");
+
+DATA(insert OID = 2731 ( asind PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 701 "701" _null_ _null_ _null_ _null_ _null_ dasind _null_ _null_ _null_ ));
+DESCR("arcsine, degrees");
+DATA(insert OID = 2732 ( acosd PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 701 "701" _null_ _null_ _null_ _null_ _null_ dacosd _null_ _null_ _null_ ));
+DESCR("arccosine, degrees");
+DATA(insert OID = 2733 ( atand PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 701 "701" _null_ _null_ _null_ _null_ _null_ datand _null_ _null_ _null_ ));
+DESCR("arctangent, degrees");
+DATA(insert OID = 2734 ( atan2d PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 701 "701 701" _null_ _null_ _null_ _null_ _null_ datan2d _null_ _null_ _null_ ));
+DESCR("arctangent, two arguments, degrees");
+DATA(insert OID = 2735 ( sind PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 701 "701" _null_ _null_ _null_ _null_ _null_ dsind _null_ _null_ _null_ ));
+DESCR("sine, degrees");
+DATA(insert OID = 2736 ( cosd PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 701 "701" _null_ _null_ _null_ _null_ _null_ dcosd _null_ _null_ _null_ ));
+DESCR("cosine, degrees");
+DATA(insert OID = 2737 ( tand PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 701 "701" _null_ _null_ _null_ _null_ _null_ dtand _null_ _null_ _null_ ));
+DESCR("tangent, degrees");
+DATA(insert OID = 2738 ( cotd PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 701 "701" _null_ _null_ _null_ _null_ _null_ dcotd _null_ _null_ _null_ ));
+DESCR("cotangent, degrees");
+
DATA(insert OID = 1608 ( degrees PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 701 "701" _null_ _null_ _null_ _null_ _null_ degrees _null_ _null_ _null_ ));
DESCR("radians to degrees");
DATA(insert OID = 1609 ( radians PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 701 "701" _null_ _null_ _null_ _null_ _null_ radians _null_ _null_ _null_ ));
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 3c134a3aa96..c2e529fc6f6 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -407,6 +407,14 @@ extern Datum dcos(PG_FUNCTION_ARGS);
extern Datum dcot(PG_FUNCTION_ARGS);
extern Datum dsin(PG_FUNCTION_ARGS);
extern Datum dtan(PG_FUNCTION_ARGS);
+extern Datum dacosd(PG_FUNCTION_ARGS);
+extern Datum dasind(PG_FUNCTION_ARGS);
+extern Datum datand(PG_FUNCTION_ARGS);
+extern Datum datan2d(PG_FUNCTION_ARGS);
+extern Datum dcosd(PG_FUNCTION_ARGS);
+extern Datum dcotd(PG_FUNCTION_ARGS);
+extern Datum dsind(PG_FUNCTION_ARGS);
+extern Datum dtand(PG_FUNCTION_ARGS);
extern Datum degrees(PG_FUNCTION_ARGS);
extern Datum dpi(PG_FUNCTION_ARGS);
extern Datum radians(PG_FUNCTION_ARGS);
diff --git a/src/test/regress/expected/float8-exp-three-digits-win32.out b/src/test/regress/expected/float8-exp-three-digits-win32.out
index 2dd648d6b92..6891ee0b4aa 100644
--- a/src/test/regress/expected/float8-exp-three-digits-win32.out
+++ b/src/test/regress/expected/float8-exp-three-digits-win32.out
@@ -444,3 +444,80 @@ SELECT '' AS five, * FROM FLOAT8_TBL;
| -1.2345678901234e-200
(5 rows)
+-- test exact cases for trigonometric functions in degrees
+SELECT x,
+ CASE WHEN sind(x) IN (-1,-0.5,0,0.5,1) THEN sind(x) END AS sind,
+ CASE WHEN cosd(x) IN (-1,-0.5,0,0.5,1) THEN cosd(x) END AS cosd,
+ CASE WHEN tand(x) IN ('-Infinity'::float8,-1,0,
+ 1,'Infinity'::float8) THEN tand(x) END AS tand,
+ CASE WHEN cotd(x) IN ('-Infinity'::float8,-1,0,
+ 1,'Infinity'::float8) THEN cotd(x) END AS cotd
+FROM generate_series(0, 360, 15) AS t(x);
+ x | sind | cosd | tand | cotd
+-----+------+------+-----------+-----------
+ 0 | 0 | 1 | 0 | Infinity
+ 15 | | | |
+ 30 | 0.5 | | |
+ 45 | | | 1 | 1
+ 60 | | 0.5 | |
+ 75 | | | |
+ 90 | 1 | 0 | Infinity | 0
+ 105 | | | |
+ 120 | | -0.5 | |
+ 135 | | | -1 | -1
+ 150 | 0.5 | | |
+ 165 | | | |
+ 180 | 0 | -1 | -0 | -Infinity
+ 195 | | | |
+ 210 | -0.5 | | |
+ 225 | | | 1 | 1
+ 240 | | -0.5 | |
+ 255 | | | |
+ 270 | -1 | 0 | -Infinity | -0
+ 285 | | | |
+ 300 | | 0.5 | |
+ 315 | | | -1 | -1
+ 330 | -0.5 | | |
+ 345 | | | |
+ 360 | 0 | 1 | 0 | Infinity
+(25 rows)
+
+SELECT x,
+ CASE WHEN asind(x) IN (-90,-30,0,30,90) THEN asind(x) END AS asind,
+ CASE WHEN acosd(x) IN (0,60,90,120,180) THEN acosd(x) END AS acosd,
+ CASE WHEN atand(x) IN (-45,0,45) THEN atand(x) END AS atand
+FROM (VALUES (-1), (-0.5), (0), (0.5), (1)) AS t(x);
+ x | asind | acosd | atand
+------+-------+-------+-------
+ -1 | -90 | 180 | -45
+ -0.5 | -30 | 120 |
+ 0 | 0 | 90 | 0
+ 0.5 | 30 | 60 |
+ 1 | 90 | 0 | 45
+(5 rows)
+
+SELECT atand('-Infinity'::float8) = -90;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT atand('Infinity'::float8) = 90;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT x, y,
+ CASE WHEN atan2d(y, x) IN (-90,0,90,180) THEN atan2d(y, x) END AS atan2d
+FROM (SELECT 10*cosd(a), 10*sind(a)
+ FROM generate_series(0, 360, 90) AS t(a)) AS t(x,y);
+ x | y | atan2d
+-----+-----+--------
+ 10 | 0 | 0
+ 0 | 10 | 90
+ -10 | 0 | 180
+ 0 | -10 | -90
+ 10 | 0 | 0
+(5 rows)
+
diff --git a/src/test/regress/expected/float8-small-is-zero.out b/src/test/regress/expected/float8-small-is-zero.out
index 5da743374c9..e158e7093a2 100644
--- a/src/test/regress/expected/float8-small-is-zero.out
+++ b/src/test/regress/expected/float8-small-is-zero.out
@@ -442,3 +442,80 @@ SELECT '' AS five, * FROM FLOAT8_TBL;
| -1.2345678901234e-200
(5 rows)
+-- test exact cases for trigonometric functions in degrees
+SELECT x,
+ CASE WHEN sind(x) IN (-1,-0.5,0,0.5,1) THEN sind(x) END AS sind,
+ CASE WHEN cosd(x) IN (-1,-0.5,0,0.5,1) THEN cosd(x) END AS cosd,
+ CASE WHEN tand(x) IN ('-Infinity'::float8,-1,0,
+ 1,'Infinity'::float8) THEN tand(x) END AS tand,
+ CASE WHEN cotd(x) IN ('-Infinity'::float8,-1,0,
+ 1,'Infinity'::float8) THEN cotd(x) END AS cotd
+FROM generate_series(0, 360, 15) AS t(x);
+ x | sind | cosd | tand | cotd
+-----+------+------+-----------+-----------
+ 0 | 0 | 1 | 0 | Infinity
+ 15 | | | |
+ 30 | 0.5 | | |
+ 45 | | | 1 | 1
+ 60 | | 0.5 | |
+ 75 | | | |
+ 90 | 1 | 0 | Infinity | 0
+ 105 | | | |
+ 120 | | -0.5 | |
+ 135 | | | -1 | -1
+ 150 | 0.5 | | |
+ 165 | | | |
+ 180 | 0 | -1 | -0 | -Infinity
+ 195 | | | |
+ 210 | -0.5 | | |
+ 225 | | | 1 | 1
+ 240 | | -0.5 | |
+ 255 | | | |
+ 270 | -1 | 0 | -Infinity | -0
+ 285 | | | |
+ 300 | | 0.5 | |
+ 315 | | | -1 | -1
+ 330 | -0.5 | | |
+ 345 | | | |
+ 360 | 0 | 1 | 0 | Infinity
+(25 rows)
+
+SELECT x,
+ CASE WHEN asind(x) IN (-90,-30,0,30,90) THEN asind(x) END AS asind,
+ CASE WHEN acosd(x) IN (0,60,90,120,180) THEN acosd(x) END AS acosd,
+ CASE WHEN atand(x) IN (-45,0,45) THEN atand(x) END AS atand
+FROM (VALUES (-1), (-0.5), (0), (0.5), (1)) AS t(x);
+ x | asind | acosd | atand
+------+-------+-------+-------
+ -1 | -90 | 180 | -45
+ -0.5 | -30 | 120 |
+ 0 | 0 | 90 | 0
+ 0.5 | 30 | 60 |
+ 1 | 90 | 0 | 45
+(5 rows)
+
+SELECT atand('-Infinity'::float8) = -90;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT atand('Infinity'::float8) = 90;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT x, y,
+ CASE WHEN atan2d(y, x) IN (-90,0,90,180) THEN atan2d(y, x) END AS atan2d
+FROM (SELECT 10*cosd(a), 10*sind(a)
+ FROM generate_series(0, 360, 90) AS t(a)) AS t(x,y);
+ x | y | atan2d
+-----+-----+--------
+ 10 | 0 | 0
+ 0 | 10 | 90
+ -10 | 0 | 180
+ 0 | -10 | -90
+ 10 | 0 | 0
+(5 rows)
+
diff --git a/src/test/regress/expected/float8-small-is-zero_1.out b/src/test/regress/expected/float8-small-is-zero_1.out
index 530842e1023..42e50a04648 100644
--- a/src/test/regress/expected/float8-small-is-zero_1.out
+++ b/src/test/regress/expected/float8-small-is-zero_1.out
@@ -442,3 +442,80 @@ SELECT '' AS five, * FROM FLOAT8_TBL;
| -1.2345678901234e-200
(5 rows)
+-- test exact cases for trigonometric functions in degrees
+SELECT x,
+ CASE WHEN sind(x) IN (-1,-0.5,0,0.5,1) THEN sind(x) END AS sind,
+ CASE WHEN cosd(x) IN (-1,-0.5,0,0.5,1) THEN cosd(x) END AS cosd,
+ CASE WHEN tand(x) IN ('-Infinity'::float8,-1,0,
+ 1,'Infinity'::float8) THEN tand(x) END AS tand,
+ CASE WHEN cotd(x) IN ('-Infinity'::float8,-1,0,
+ 1,'Infinity'::float8) THEN cotd(x) END AS cotd
+FROM generate_series(0, 360, 15) AS t(x);
+ x | sind | cosd | tand | cotd
+-----+------+------+-----------+-----------
+ 0 | 0 | 1 | 0 | Infinity
+ 15 | | | |
+ 30 | 0.5 | | |
+ 45 | | | 1 | 1
+ 60 | | 0.5 | |
+ 75 | | | |
+ 90 | 1 | 0 | Infinity | 0
+ 105 | | | |
+ 120 | | -0.5 | |
+ 135 | | | -1 | -1
+ 150 | 0.5 | | |
+ 165 | | | |
+ 180 | 0 | -1 | -0 | -Infinity
+ 195 | | | |
+ 210 | -0.5 | | |
+ 225 | | | 1 | 1
+ 240 | | -0.5 | |
+ 255 | | | |
+ 270 | -1 | 0 | -Infinity | -0
+ 285 | | | |
+ 300 | | 0.5 | |
+ 315 | | | -1 | -1
+ 330 | -0.5 | | |
+ 345 | | | |
+ 360 | 0 | 1 | 0 | Infinity
+(25 rows)
+
+SELECT x,
+ CASE WHEN asind(x) IN (-90,-30,0,30,90) THEN asind(x) END AS asind,
+ CASE WHEN acosd(x) IN (0,60,90,120,180) THEN acosd(x) END AS acosd,
+ CASE WHEN atand(x) IN (-45,0,45) THEN atand(x) END AS atand
+FROM (VALUES (-1), (-0.5), (0), (0.5), (1)) AS t(x);
+ x | asind | acosd | atand
+------+-------+-------+-------
+ -1 | -90 | 180 | -45
+ -0.5 | -30 | 120 |
+ 0 | 0 | 90 | 0
+ 0.5 | 30 | 60 |
+ 1 | 90 | 0 | 45
+(5 rows)
+
+SELECT atand('-Infinity'::float8) = -90;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT atand('Infinity'::float8) = 90;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT x, y,
+ CASE WHEN atan2d(y, x) IN (-90,0,90,180) THEN atan2d(y, x) END AS atan2d
+FROM (SELECT 10*cosd(a), 10*sind(a)
+ FROM generate_series(0, 360, 90) AS t(a)) AS t(x,y);
+ x | y | atan2d
+-----+-----+--------
+ 10 | 0 | 0
+ 0 | 10 | 90
+ -10 | 0 | 180
+ 0 | -10 | -90
+ 10 | 0 | 0
+(5 rows)
+
diff --git a/src/test/regress/expected/float8.out b/src/test/regress/expected/float8.out
index 6221538af5c..b77b34f2b30 100644
--- a/src/test/regress/expected/float8.out
+++ b/src/test/regress/expected/float8.out
@@ -444,3 +444,80 @@ SELECT '' AS five, * FROM FLOAT8_TBL;
| -1.2345678901234e-200
(5 rows)
+-- test exact cases for trigonometric functions in degrees
+SELECT x,
+ CASE WHEN sind(x) IN (-1,-0.5,0,0.5,1) THEN sind(x) END AS sind,
+ CASE WHEN cosd(x) IN (-1,-0.5,0,0.5,1) THEN cosd(x) END AS cosd,
+ CASE WHEN tand(x) IN ('-Infinity'::float8,-1,0,
+ 1,'Infinity'::float8) THEN tand(x) END AS tand,
+ CASE WHEN cotd(x) IN ('-Infinity'::float8,-1,0,
+ 1,'Infinity'::float8) THEN cotd(x) END AS cotd
+FROM generate_series(0, 360, 15) AS t(x);
+ x | sind | cosd | tand | cotd
+-----+------+------+-----------+-----------
+ 0 | 0 | 1 | 0 | Infinity
+ 15 | | | |
+ 30 | 0.5 | | |
+ 45 | | | 1 | 1
+ 60 | | 0.5 | |
+ 75 | | | |
+ 90 | 1 | 0 | Infinity | 0
+ 105 | | | |
+ 120 | | -0.5 | |
+ 135 | | | -1 | -1
+ 150 | 0.5 | | |
+ 165 | | | |
+ 180 | 0 | -1 | -0 | -Infinity
+ 195 | | | |
+ 210 | -0.5 | | |
+ 225 | | | 1 | 1
+ 240 | | -0.5 | |
+ 255 | | | |
+ 270 | -1 | 0 | -Infinity | -0
+ 285 | | | |
+ 300 | | 0.5 | |
+ 315 | | | -1 | -1
+ 330 | -0.5 | | |
+ 345 | | | |
+ 360 | 0 | 1 | 0 | Infinity
+(25 rows)
+
+SELECT x,
+ CASE WHEN asind(x) IN (-90,-30,0,30,90) THEN asind(x) END AS asind,
+ CASE WHEN acosd(x) IN (0,60,90,120,180) THEN acosd(x) END AS acosd,
+ CASE WHEN atand(x) IN (-45,0,45) THEN atand(x) END AS atand
+FROM (VALUES (-1), (-0.5), (0), (0.5), (1)) AS t(x);
+ x | asind | acosd | atand
+------+-------+-------+-------
+ -1 | -90 | 180 | -45
+ -0.5 | -30 | 120 |
+ 0 | 0 | 90 | 0
+ 0.5 | 30 | 60 |
+ 1 | 90 | 0 | 45
+(5 rows)
+
+SELECT atand('-Infinity'::float8) = -90;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT atand('Infinity'::float8) = 90;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT x, y,
+ CASE WHEN atan2d(y, x) IN (-90,0,90,180) THEN atan2d(y, x) END AS atan2d
+FROM (SELECT 10*cosd(a), 10*sind(a)
+ FROM generate_series(0, 360, 90) AS t(a)) AS t(x,y);
+ x | y | atan2d
+-----+-----+--------
+ 10 | 0 | 0
+ 0 | 10 | 90
+ -10 | 0 | 180
+ 0 | -10 | -90
+ 10 | 0 | 0
+(5 rows)
+
diff --git a/src/test/regress/sql/float8.sql b/src/test/regress/sql/float8.sql
index 92a574ab7bf..45a484b8b95 100644
--- a/src/test/regress/sql/float8.sql
+++ b/src/test/regress/sql/float8.sql
@@ -167,3 +167,27 @@ INSERT INTO FLOAT8_TBL(f1) VALUES ('-1.2345678901234e+200');
INSERT INTO FLOAT8_TBL(f1) VALUES ('-1.2345678901234e-200');
SELECT '' AS five, * FROM FLOAT8_TBL;
+
+-- test exact cases for trigonometric functions in degrees
+SELECT x,
+ CASE WHEN sind(x) IN (-1,-0.5,0,0.5,1) THEN sind(x) END AS sind,
+ CASE WHEN cosd(x) IN (-1,-0.5,0,0.5,1) THEN cosd(x) END AS cosd,
+ CASE WHEN tand(x) IN ('-Infinity'::float8,-1,0,
+ 1,'Infinity'::float8) THEN tand(x) END AS tand,
+ CASE WHEN cotd(x) IN ('-Infinity'::float8,-1,0,
+ 1,'Infinity'::float8) THEN cotd(x) END AS cotd
+FROM generate_series(0, 360, 15) AS t(x);
+
+SELECT x,
+ CASE WHEN asind(x) IN (-90,-30,0,30,90) THEN asind(x) END AS asind,
+ CASE WHEN acosd(x) IN (0,60,90,120,180) THEN acosd(x) END AS acosd,
+ CASE WHEN atand(x) IN (-45,0,45) THEN atand(x) END AS atand
+FROM (VALUES (-1), (-0.5), (0), (0.5), (1)) AS t(x);
+
+SELECT atand('-Infinity'::float8) = -90;
+SELECT atand('Infinity'::float8) = 90;
+
+SELECT x, y,
+ CASE WHEN atan2d(y, x) IN (-90,0,90,180) THEN atan2d(y, x) END AS atan2d
+FROM (SELECT 10*cosd(a), 10*sind(a)
+ FROM generate_series(0, 360, 90) AS t(a)) AS t(x,y);