summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane2016-01-22 20:46:22 +0000
committerTom Lane2016-01-22 20:46:22 +0000
commite1bd684a34c11139a1bf4e5200c3bbe59a0fbfad (patch)
tree821061660ece1adfe3fdffd2b15b7c352809f245
parentfd5200c3dca0bc725f5848eef7ffff538f4479ed (diff)
Add trigonometric functions that work in degrees.
The implementations go to some lengths to deliver exact results for values where an exact result can be expected, such as sind(30) = 0.5 exactly. Dean Rasheed, reviewed by Michael Paquier
-rw-r--r--doc/src/sgml/func.sgml72
-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
10 files changed, 860 insertions, 9 deletions
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 4d2b88fafd..9c143b2a63 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -1006,20 +1006,19 @@
Finally, <xref linkend="functions-math-trig-table"> shows the
available trigonometric functions. All trigonometric functions
take arguments and return values of type <type>double
- precision</type>. Trigonometric functions arguments are expressed
- in radians. Inverse functions return values are expressed in
- radians. See unit transformation functions
- <literal><function>radians()</function></literal> and
- <literal><function>degrees()</function></literal> above.
+ precision</type>. Each of the trigonometric functions comes in
+ two variants, one that measures angles in radians and one that
+ measures angles in degrees.
</para>
<table id="functions-math-trig-table">
<title>Trigonometric Functions</title>
- <tgroup cols="2">
+ <tgroup cols="3">
<thead>
<row>
- <entry>Function</entry>
+ <entry>Function (radians)</entry>
+ <entry>Function (degrees)</entry>
<entry>Description</entry>
</row>
</thead>
@@ -1031,6 +1030,11 @@
<primary>acos</primary>
</indexterm><literal><function>acos(<replaceable>x</replaceable>)</function></literal>
</entry>
+ <entry>
+ <indexterm>
+ <primary>acosd</primary>
+ </indexterm><literal><function>acosd(<replaceable>x</replaceable>)</function></literal>
+ </entry>
<entry>inverse cosine</entry>
</row>
@@ -1041,6 +1045,12 @@
</indexterm>
<literal><function>asin(<replaceable>x</replaceable>)</function></literal>
</entry>
+ <entry>
+ <indexterm>
+ <primary>asind</primary>
+ </indexterm>
+ <literal><function>asind(<replaceable>x</replaceable>)</function></literal>
+ </entry>
<entry>inverse sine</entry>
</row>
@@ -1051,6 +1061,12 @@
</indexterm>
<literal><function>atan(<replaceable>x</replaceable>)</function></literal>
</entry>
+ <entry>
+ <indexterm>
+ <primary>atand</primary>
+ </indexterm>
+ <literal><function>atand(<replaceable>x</replaceable>)</function></literal>
+ </entry>
<entry>inverse tangent</entry>
</row>
@@ -1062,6 +1078,13 @@
<literal><function>atan2(<replaceable>y</replaceable>,
<replaceable>x</replaceable>)</function></literal>
</entry>
+ <entry>
+ <indexterm>
+ <primary>atan2d</primary>
+ </indexterm>
+ <literal><function>atan2d(<replaceable>y</replaceable>,
+ <replaceable>x</replaceable>)</function></literal>
+ </entry>
<entry>inverse tangent of
<literal><replaceable>y</replaceable>/<replaceable>x</replaceable></literal></entry>
</row>
@@ -1073,6 +1096,12 @@
</indexterm>
<literal><function>cos(<replaceable>x</replaceable>)</function></literal>
</entry>
+ <entry>
+ <indexterm>
+ <primary>cosd</primary>
+ </indexterm>
+ <literal><function>cosd(<replaceable>x</replaceable>)</function></literal>
+ </entry>
<entry>cosine</entry>
</row>
@@ -1083,6 +1112,12 @@
</indexterm>
<literal><function>cot(<replaceable>x</replaceable>)</function></literal>
</entry>
+ <entry>
+ <indexterm>
+ <primary>cotd</primary>
+ </indexterm>
+ <literal><function>cotd(<replaceable>x</replaceable>)</function></literal>
+ </entry>
<entry>cotangent</entry>
</row>
@@ -1093,6 +1128,12 @@
</indexterm>
<literal><function>sin(<replaceable>x</replaceable>)</function></literal>
</entry>
+ <entry>
+ <indexterm>
+ <primary>sind</primary>
+ </indexterm>
+ <literal><function>sind(<replaceable>x</replaceable>)</function></literal>
+ </entry>
<entry>sine</entry>
</row>
@@ -1103,12 +1144,29 @@
</indexterm>
<literal><function>tan(<replaceable>x</replaceable>)</function></literal>
</entry>
+ <entry>
+ <indexterm>
+ <primary>tand</primary>
+ </indexterm>
+ <literal><function>tand(<replaceable>x</replaceable>)</function></literal>
+ </entry>
<entry>tangent</entry>
</row>
</tbody>
</tgroup>
</table>
+ <note>
+ <para>
+ Another way to work with angles measured in degrees is to use the unit
+ transformation functions <literal><function>radians()</function></literal>
+ and <literal><function>degrees()</function></literal> shown earlier.
+ However, using the degree-based trigonometric functions is preferred,
+ as that way avoids roundoff error for special cases such
+ as <literal>sind(30)</>.
+ </para>
+ </note>
+
</sect1>
diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c
index a3a989ed28..51e996ceb2 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 58709f56ac..894a619f94 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 244aa4d016..79e92ffa78 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 3c134a3aa9..c2e529fc6f 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 2dd648d6b9..6891ee0b4a 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 5da743374c..e158e7093a 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 530842e102..42e50a0464 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 6221538af5..b77b34f2b3 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 92a574ab7b..45a484b8b9 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);