On Tue, Nov 10, 2015 at 11:17 PM, Michael Paquier <michael.paqu...@gmail.com>
wrote:
> On Sun, Nov 1, 2015 at 9:34 PM, Dean Rasheed <dean.a.rash...@gmail.com>
wrote:
>> On 27 October 2015 at 08:24, Dean Rasheed <dean.a.rash...@gmail.com>
wrote:
>>> I think it's still feasible to have sind(30) = 0.5 exactly and keep
>>> monotonicity....
>>>
>>
>> Here's a patch along those lines. It turned out to be fairly
>> straightforward. It's still basically a thin wrapper on top of the
>> math library trig functions, with a bit of careful scaling to ensure
>> that curves join together to form continuous functions that are
>> monotonic in the expected regions and return exact values in all the
>> special cases 0,30,45,60,90,...
>>
>> I also modified some of the CHECKFLOATVAL() checks which didn't look
>> right to me, unless there's some odd platform-specific behaviour that
>> I'm not aware of, functions like sin and asin should never return
>> infinity.

-       CHECKFLOATVAL(result, isinf(arg1), true);
+       CHECKFLOATVAL(result, false, true);
        PG_RETURN_FLOAT8(result);

Hm. I would let them as-is, and update your patch to do the similar checks
in the new functions introduced. See f9ac414 from 2007 which is the result
of the following thread:
http://www.postgresql.org/message-id/200612271844.kbriivb18...@momjian.us
It doesn't seem wise to be backward regarding those Inf/NaN checks.

> The alternative expected outputs for test float8 need to be updated,
> this is causing regression failures particularly on win32 where 3
> digits are used for exponentials and where tan('NaN') actually results
> in an ERROR. See for example the attached regressions.diffs.

It would be nice to split the results specific to NaN and Infinity into
separate queries. The test for arctangent is one where things should be
splitted.

c5e86ea took some of the OIDs of this previous patch, so I rebased it as
attached.

        result = 1.0 / result;
-       CHECKFLOATVAL(result, true /* cotan(pi/2) == inf */ , true);
+       CHECKFLOATVAL(result, true /* cot(0) == Inf */ , true);
        PG_RETURN_FLOAT8(result);
This one is true. it could be corrected as an independent fix.

+       if (isinf(arg1) || arg1 < -1 || arg1 > 1)
+               ereport(ERROR,
+
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+                                errmsg("input is out of range")));
This should check on -1.0 and 1.0?

+       if (arg1 > 90)
+       {
+               /* tand(180-x) = -tand(x) */
+               arg1 = 180 - arg1;
+               sign = -sign;
+       }
Similarly the series of checks in atand, dcosd, asind, should use .0
precision points? Same remark for other code paths like cosd_0_to_60 for
example.

+       if (arg1 > 180)
+       {
+               /* tand(360-x) = -tand(x) */
+        arg1 = 360 - arg1;
+               sign = -sign;
+       }
Picky detail: be careful of incorrect tab spaces.

=# select acos(-1.1);
 acos
------
  NaN
(1 row)
=# select acosd(-1.1);
ERROR:  22003: input is out of range
LOCATION:  dacosd, float.c:1752
Some results are inconsistent, it seems that we should return NaN in the
second case instead of an error.

I had as well a look at the monotony of those functions, using rough
queries like this one, and things are looking nice. The precise values are
taken into account and their surroundings are monotone.
with degrees as (
select generate_series(89.999999998, 90.000000002, 0.000000001)
union all select generate_series(44.999999998, 45.000000002, 0.000000001)
union all select generate_series(29.999999998, 30.000000002, 0.000000001)
union all select generate_series(-0.000000002, 0.000000002, 0.000000001)
union all select generate_series(59.999999998, 60.000000002, 0.000000001))
SELECT x, cosd(x), sind(x), tand(x) FROM degrees as deg(x);
with degrees as (
select generate_series((sqrt(3) / 3 - 0.00001)::numeric, (sqrt(3) / 3 +
0.00001)::numeric, 0.000001)
union all select generate_series((sqrt(3) / 2 - 0.00001)::numeric, (sqrt(3)
/ 2 + 0.00001)::numeric, 0.000001)
union all select generate_series(0.5 - 0.00001, 0.5 + 0.00001, 0.000001)
union all select generate_series(0, 0.00001, 0.000001)
union all select generate_series(0.99999, 1, 0.000001))
select x, acosd(x), asind(x), atand(x) from degrees as deg(x);
Attached are the results of all those things if others want to have a look.
Regards,
-- 
Michael

Attachment: degrees.sql
Description: Binary data

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 60b9a09..3716210 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -993,9 +993,9 @@
    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
+   precision</type>.  Each of the trigonometric functions comes in
+   two varieties, one which works in radians and one which works in
+   degrees.  See unit transformation functions
    <literal><function>radians()</function></literal> and
    <literal><function>degrees()</function></literal> above.
   </para>
@@ -1003,10 +1003,11 @@
    <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>
@@ -1018,6 +1019,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>
 
@@ -1028,6 +1034,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>
 
@@ -1038,6 +1050,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>
 
@@ -1049,6 +1067,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>
@@ -1060,6 +1085,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>
 
@@ -1070,6 +1101,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>
 
@@ -1080,6 +1117,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>
 
@@ -1090,6 +1133,12 @@
         </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>
diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c
index 4e927d8..d2318f7 100644
--- a/src/backend/utils/adt/float.c
+++ b/src/backend/utils/adt/float.c
@@ -1535,7 +1535,7 @@ dacos(PG_FUNCTION_ARGS)
 				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
 				 errmsg("input is out of range")));
 
-	CHECKFLOATVAL(result, isinf(arg1), true);
+	CHECKFLOATVAL(result, false, true);
 	PG_RETURN_FLOAT8(result);
 }
 
@@ -1556,7 +1556,7 @@ dasin(PG_FUNCTION_ARGS)
 				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
 				 errmsg("input is out of range")));
 
-	CHECKFLOATVAL(result, isinf(arg1), true);
+	CHECKFLOATVAL(result, false, true);
 	PG_RETURN_FLOAT8(result);
 }
 
@@ -1577,7 +1577,7 @@ datan(PG_FUNCTION_ARGS)
 				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
 				 errmsg("input is out of range")));
 
-	CHECKFLOATVAL(result, isinf(arg1), true);
+	CHECKFLOATVAL(result, false, true);
 	PG_RETURN_FLOAT8(result);
 }
 
@@ -1599,7 +1599,7 @@ datan2(PG_FUNCTION_ARGS)
 				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
 				 errmsg("input is out of range")));
 
-	CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2), true);
+	CHECKFLOATVAL(result, false, true);
 	PG_RETURN_FLOAT8(result);
 }
 
@@ -1620,7 +1620,7 @@ dcos(PG_FUNCTION_ARGS)
 				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
 				 errmsg("input is out of range")));
 
-	CHECKFLOATVAL(result, isinf(arg1), true);
+	CHECKFLOATVAL(result, false, true);
 	PG_RETURN_FLOAT8(result);
 }
 
@@ -1642,7 +1642,7 @@ dcot(PG_FUNCTION_ARGS)
 				 errmsg("input is out of range")));
 
 	result = 1.0 / result;
-	CHECKFLOATVAL(result, true /* cotan(pi/2) == inf */ , true);
+	CHECKFLOATVAL(result, true /* cot(0) == Inf */ , true);
 	PG_RETURN_FLOAT8(result);
 }
 
@@ -1663,7 +1663,7 @@ dsin(PG_FUNCTION_ARGS)
 				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
 				 errmsg("input is out of range")));
 
-	CHECKFLOATVAL(result, isinf(arg1), true);
+	CHECKFLOATVAL(result, false, true);
 	PG_RETURN_FLOAT8(result);
 }
 
@@ -1690,6 +1690,368 @@ dtan(PG_FUNCTION_ARGS)
 
 
 /*
+ *		asind_q1		- returns the inverse sine of x in degrees, where x is
+ *						  assumed to be in the range [0, 1] and the result is
+ *						  an angle in the first quadrant (0 to 90 degrees).
+ *
+ * In this quadrant there are 3 special case inputs (0, 0.5 and 1) for which
+ * this function will return exact values (0, 30 and 90 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;
+	else
+		return 90 - (acos(x) / acos(0.5)) * 60;
+}
+
+
+/*
+ *		acosd_q1		- returns the inverse cosine of x in degrees, where x
+ *						  is assumed to be in the range [0, 1] and the result
+ *						  is an angle in the first quadrant (0 to 90 degrees).
+ *
+ * In this quadrant there are 3 special case inputs (0, 0.5 and 1) for which
+ * this function will return exact values (0, 60 and 90 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 - (asin(x) / asin(0.5)) * 30;
+	else
+		return (acos(x) / acos(0.5)) * 60;
+}
+
+
+/*
+ *		dacosd			- returns the arccos of arg1 (degrees)
+ */
+Datum
+dacosd(PG_FUNCTION_ARGS)
+{
+	float8		arg1 = PG_GETARG_FLOAT8(0);
+	float8		result;
+
+	if (isinf(arg1) || arg1 < -1 || arg1 > 1)
+		ereport(ERROR,
+				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+				 errmsg("input is out of range")));
+
+	if (isnan(arg1))
+		result = arg1;
+	else if (arg1 >= 0)
+		result = acosd_q1(arg1);
+	else
+		result = 90 + 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;
+
+	if (isinf(arg1) || arg1 < -1 || arg1 > 1)
+		ereport(ERROR,
+				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+				 errmsg("input is out of range")));
+
+	if (isnan(arg1))
+		result = arg1;
+	else if (arg1 >= 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;
+
+	errno = 0;
+	result = atan(arg1);
+	if (errno != 0)
+		ereport(ERROR,
+				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+				 errmsg("input is out of range")));
+
+	result = (result / atan(1.0)) * 45;
+	CHECKFLOATVAL(result, false, true);
+	PG_RETURN_FLOAT8(result);
+}
+
+
+/*
+ *		atan2d			- returns the arctan2 of arg1 (degrees)
+ */
+Datum
+datan2d(PG_FUNCTION_ARGS)
+{
+	float8		arg1 = PG_GETARG_FLOAT8(0);
+	float8		arg2 = PG_GETARG_FLOAT8(1);
+	float8		result;
+
+	errno = 0;
+	result = atan2(arg1, arg2);
+	if (errno != 0)
+		ereport(ERROR,
+				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+				 errmsg("input is out of range")));
+
+	result = (result / atan(1.0)) * 45;
+	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)) / sin(30 * (M_PI/180)) ) / 2;
+}
+
+
+/*
+ *		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 ( 2 - (1 - cos(x * (M_PI/180))) / (1 - cos(60 * (M_PI/180))) ) / 2;
+}
+
+
+/*
+ *		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.
+	 */
+	return x <= 30 ? sind_0_to_30(x) : cosd_0_to_60(90-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.
+	 */
+	return x <= 60 ? cosd_0_to_60(x) : sind_0_to_30(90-x);
+}
+
+
+/*
+ *		dcosd			- returns the cosine of arg1 (degrees)
+ */
+Datum
+dcosd(PG_FUNCTION_ARGS)
+{
+	float8		arg1 = PG_GETARG_FLOAT8(0);
+	int			sign = 1;
+	float8		result;
+
+	/* Reduce the range of the input to [0,90] degrees */
+	arg1 = fmod(arg1, 360.0);
+	if (isnan(arg1))
+		PG_RETURN_FLOAT8(arg1);
+
+	if (arg1 < 0)
+		/* cosd(-x) = cosd(x) */
+		arg1 = -arg1;
+
+	if (arg1 > 180)
+		/* cosd(360-x) = cosd(x) */
+		arg1 = 360 - arg1;
+
+	if (arg1 > 90)
+	{
+		/* cosd(180-x) = -cosd(x) */
+		arg1 = 180 - 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;
+
+	/* Reduce the range of the input to [0,90] degrees */
+	arg1 = fmod(arg1, 360.0);
+	if (isnan(arg1))
+		PG_RETURN_FLOAT8(arg1);
+
+	if (arg1 < 0)
+	{
+		/* cotd(-x) = -cotd(x) */
+		arg1 = -arg1;
+		sign = -sign;
+	}
+
+	if (arg1 > 180)
+	{
+		/* cotd(360-x) = -cotd(x) */
+		arg1 = 360 - arg1;
+		sign = -sign;
+	}
+
+	if (arg1 > 90)
+	{
+		/* cotd(180-x) = -cotd(x) */
+		arg1 = 180 - 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;
+
+	/* Reduce the range of the input to [0,90] degrees */
+	arg1 = fmod(arg1, 360.0);
+	if (isnan(arg1))
+		PG_RETURN_FLOAT8(arg1);
+
+	if (arg1 < 0)
+	{
+		/* sind(-x) = -sind(x) */
+		arg1 = -arg1;
+		sign = -sign;
+	}
+
+	if (arg1 > 180)
+	{
+		/* sind(360-x) = -sind(x) */
+		arg1 = 360 - arg1;
+		sign = -sign;
+	}
+
+	if (arg1 > 90)
+		/* sind(180-x) = sind(x) */
+		arg1 = 180 - 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;
+
+	/* Reduce the range of the input to [0,90] degrees */
+	arg1 = fmod(arg1, 360.0);
+	if (isnan(arg1))
+		PG_RETURN_FLOAT8(arg1);
+
+	if (arg1 < 0)
+	{
+		/* tand(-x) = -tand(x) */
+		arg1 = -arg1;
+		sign = -sign;
+	}
+
+	if (arg1 > 180)
+	{
+		/* tand(360-x) = -tand(x) */
+        arg1 = 360 - arg1;
+		sign = -sign;
+	}
+
+	if (arg1 > 90)
+	{
+		/* tand(180-x) = -tand(x) */
+		arg1 = 180 - 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/pg_proc.h b/src/include/catalog/pg_proc.h
index d8640db..f4d468e 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -1894,6 +1894,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 = 3317 (  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 = 3318 (  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 = 3319 (  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 = 3320 (  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 = 3321 (  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 = 3322 (  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 = 3323 (  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 = 3324 (  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 e610bf3..cbca7f3 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.out b/src/test/regress/expected/float8.out
index 6221538..a6261d2 100644
--- a/src/test/regress/expected/float8.out
+++ b/src/test/regress/expected/float8.out
@@ -444,3 +444,330 @@ SELECT '' AS five, * FROM FLOAT8_TBL;
       | -1.2345678901234e-200
 (5 rows)
 
+-- Test the trigonometric functions.
+-- Use a lower precision to eliminate variations between platforms.
+SET extra_float_digits TO -5;
+-- sine and cosine
+SELECT x,
+       sin(radians(x)) AS sin_radians, sind(x),
+       cos(radians(x)) AS cos_radians, cosd(x)
+FROM generate_series(-360, 360, 10) AS t(x)
+WHERE x = 0 OR x % 90 != 0; -- skip tests that are very platform-dependent
+  x   |  sin_radians  |     sind      |  cos_radians  |     cosd      
+------+---------------+---------------+---------------+---------------
+ -350 |  0.1736481777 |  0.1736481777 |   0.984807753 |   0.984807753
+ -340 |  0.3420201433 |  0.3420201433 |  0.9396926208 |  0.9396926208
+ -330 |           0.5 |           0.5 |  0.8660254038 |  0.8660254038
+ -320 |  0.6427876097 |  0.6427876097 |  0.7660444431 |  0.7660444431
+ -310 |  0.7660444431 |  0.7660444431 |  0.6427876097 |  0.6427876097
+ -300 |  0.8660254038 |  0.8660254038 |           0.5 |           0.5
+ -290 |  0.9396926208 |  0.9396926208 |  0.3420201433 |  0.3420201433
+ -280 |   0.984807753 |   0.984807753 |  0.1736481777 |  0.1736481777
+ -260 |   0.984807753 |   0.984807753 | -0.1736481777 | -0.1736481777
+ -250 |  0.9396926208 |  0.9396926208 | -0.3420201433 | -0.3420201433
+ -240 |  0.8660254038 |  0.8660254038 |          -0.5 |          -0.5
+ -230 |  0.7660444431 |  0.7660444431 | -0.6427876097 | -0.6427876097
+ -220 |  0.6427876097 |  0.6427876097 | -0.7660444431 | -0.7660444431
+ -210 |           0.5 |           0.5 | -0.8660254038 | -0.8660254038
+ -200 |  0.3420201433 |  0.3420201433 | -0.9396926208 | -0.9396926208
+ -190 |  0.1736481777 |  0.1736481777 |  -0.984807753 |  -0.984807753
+ -170 | -0.1736481777 | -0.1736481777 |  -0.984807753 |  -0.984807753
+ -160 | -0.3420201433 | -0.3420201433 | -0.9396926208 | -0.9396926208
+ -150 |          -0.5 |          -0.5 | -0.8660254038 | -0.8660254038
+ -140 | -0.6427876097 | -0.6427876097 | -0.7660444431 | -0.7660444431
+ -130 | -0.7660444431 | -0.7660444431 | -0.6427876097 | -0.6427876097
+ -120 | -0.8660254038 | -0.8660254038 |          -0.5 |          -0.5
+ -110 | -0.9396926208 | -0.9396926208 | -0.3420201433 | -0.3420201433
+ -100 |  -0.984807753 |  -0.984807753 | -0.1736481777 | -0.1736481777
+  -80 |  -0.984807753 |  -0.984807753 |  0.1736481777 |  0.1736481777
+  -70 | -0.9396926208 | -0.9396926208 |  0.3420201433 |  0.3420201433
+  -60 | -0.8660254038 | -0.8660254038 |           0.5 |           0.5
+  -50 | -0.7660444431 | -0.7660444431 |  0.6427876097 |  0.6427876097
+  -40 | -0.6427876097 | -0.6427876097 |  0.7660444431 |  0.7660444431
+  -30 |          -0.5 |          -0.5 |  0.8660254038 |  0.8660254038
+  -20 | -0.3420201433 | -0.3420201433 |  0.9396926208 |  0.9396926208
+  -10 | -0.1736481777 | -0.1736481777 |   0.984807753 |   0.984807753
+    0 |             0 |             0 |             1 |             1
+   10 |  0.1736481777 |  0.1736481777 |   0.984807753 |   0.984807753
+   20 |  0.3420201433 |  0.3420201433 |  0.9396926208 |  0.9396926208
+   30 |           0.5 |           0.5 |  0.8660254038 |  0.8660254038
+   40 |  0.6427876097 |  0.6427876097 |  0.7660444431 |  0.7660444431
+   50 |  0.7660444431 |  0.7660444431 |  0.6427876097 |  0.6427876097
+   60 |  0.8660254038 |  0.8660254038 |           0.5 |           0.5
+   70 |  0.9396926208 |  0.9396926208 |  0.3420201433 |  0.3420201433
+   80 |   0.984807753 |   0.984807753 |  0.1736481777 |  0.1736481777
+  100 |   0.984807753 |   0.984807753 | -0.1736481777 | -0.1736481777
+  110 |  0.9396926208 |  0.9396926208 | -0.3420201433 | -0.3420201433
+  120 |  0.8660254038 |  0.8660254038 |          -0.5 |          -0.5
+  130 |  0.7660444431 |  0.7660444431 | -0.6427876097 | -0.6427876097
+  140 |  0.6427876097 |  0.6427876097 | -0.7660444431 | -0.7660444431
+  150 |           0.5 |           0.5 | -0.8660254038 | -0.8660254038
+  160 |  0.3420201433 |  0.3420201433 | -0.9396926208 | -0.9396926208
+  170 |  0.1736481777 |  0.1736481777 |  -0.984807753 |  -0.984807753
+  190 | -0.1736481777 | -0.1736481777 |  -0.984807753 |  -0.984807753
+  200 | -0.3420201433 | -0.3420201433 | -0.9396926208 | -0.9396926208
+  210 |          -0.5 |          -0.5 | -0.8660254038 | -0.8660254038
+  220 | -0.6427876097 | -0.6427876097 | -0.7660444431 | -0.7660444431
+  230 | -0.7660444431 | -0.7660444431 | -0.6427876097 | -0.6427876097
+  240 | -0.8660254038 | -0.8660254038 |          -0.5 |          -0.5
+  250 | -0.9396926208 | -0.9396926208 | -0.3420201433 | -0.3420201433
+  260 |  -0.984807753 |  -0.984807753 | -0.1736481777 | -0.1736481777
+  280 |  -0.984807753 |  -0.984807753 |  0.1736481777 |  0.1736481777
+  290 | -0.9396926208 | -0.9396926208 |  0.3420201433 |  0.3420201433
+  300 | -0.8660254038 | -0.8660254038 |           0.5 |           0.5
+  310 | -0.7660444431 | -0.7660444431 |  0.6427876097 |  0.6427876097
+  320 | -0.6427876097 | -0.6427876097 |  0.7660444431 |  0.7660444431
+  330 |          -0.5 |          -0.5 |  0.8660254038 |  0.8660254038
+  340 | -0.3420201433 | -0.3420201433 |  0.9396926208 |  0.9396926208
+  350 | -0.1736481777 | -0.1736481777 |   0.984807753 |   0.984807753
+(65 rows)
+
+-- test exactness
+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
+FROM generate_series(0, 360, 30) AS t(x);
+  x  | sind | cosd 
+-----+------+------
+   0 |    0 |    1
+  30 |  0.5 |     
+  60 |      |  0.5
+  90 |    1 |    0
+ 120 |      | -0.5
+ 150 |  0.5 |     
+ 180 |    0 |   -1
+ 210 | -0.5 |     
+ 240 |      | -0.5
+ 270 |   -1 |    0
+ 300 |      |  0.5
+ 330 | -0.5 |     
+ 360 |    0 |    1
+(13 rows)
+
+-- tangent and cotangent
+SELECT x,
+       tan(radians(x)) AS tan_radians, tand(x),
+       cot(radians(x)) AS cot_radians, cotd(x)
+FROM generate_series(-360, 360, 10) AS t(x)
+WHERE x = 0 OR x % 90 != 0; -- skip tests that are very platform-dependent
+  x   |  tan_radians  |     tand      |  cot_radians  |     cotd      
+------+---------------+---------------+---------------+---------------
+ -350 |  0.1763269807 |  0.1763269807 |    5.67128182 |    5.67128182
+ -340 |  0.3639702343 |  0.3639702343 |   2.747477419 |   2.747477419
+ -330 |  0.5773502692 |  0.5773502692 |   1.732050808 |   1.732050808
+ -320 |  0.8390996312 |  0.8390996312 |   1.191753593 |   1.191753593
+ -310 |   1.191753593 |   1.191753593 |  0.8390996312 |  0.8390996312
+ -300 |   1.732050808 |   1.732050808 |  0.5773502692 |  0.5773502692
+ -290 |   2.747477419 |   2.747477419 |  0.3639702343 |  0.3639702343
+ -280 |    5.67128182 |    5.67128182 |  0.1763269807 |  0.1763269807
+ -260 |   -5.67128182 |   -5.67128182 | -0.1763269807 | -0.1763269807
+ -250 |  -2.747477419 |  -2.747477419 | -0.3639702343 | -0.3639702343
+ -240 |  -1.732050808 |  -1.732050808 | -0.5773502692 | -0.5773502692
+ -230 |  -1.191753593 |  -1.191753593 | -0.8390996312 | -0.8390996312
+ -220 | -0.8390996312 | -0.8390996312 |  -1.191753593 |  -1.191753593
+ -210 | -0.5773502692 | -0.5773502692 |  -1.732050808 |  -1.732050808
+ -200 | -0.3639702343 | -0.3639702343 |  -2.747477419 |  -2.747477419
+ -190 | -0.1763269807 | -0.1763269807 |   -5.67128182 |   -5.67128182
+ -170 |  0.1763269807 |  0.1763269807 |    5.67128182 |    5.67128182
+ -160 |  0.3639702343 |  0.3639702343 |   2.747477419 |   2.747477419
+ -150 |  0.5773502692 |  0.5773502692 |   1.732050808 |   1.732050808
+ -140 |  0.8390996312 |  0.8390996312 |   1.191753593 |   1.191753593
+ -130 |   1.191753593 |   1.191753593 |  0.8390996312 |  0.8390996312
+ -120 |   1.732050808 |   1.732050808 |  0.5773502692 |  0.5773502692
+ -110 |   2.747477419 |   2.747477419 |  0.3639702343 |  0.3639702343
+ -100 |    5.67128182 |    5.67128182 |  0.1763269807 |  0.1763269807
+  -80 |   -5.67128182 |   -5.67128182 | -0.1763269807 | -0.1763269807
+  -70 |  -2.747477419 |  -2.747477419 | -0.3639702343 | -0.3639702343
+  -60 |  -1.732050808 |  -1.732050808 | -0.5773502692 | -0.5773502692
+  -50 |  -1.191753593 |  -1.191753593 | -0.8390996312 | -0.8390996312
+  -40 | -0.8390996312 | -0.8390996312 |  -1.191753593 |  -1.191753593
+  -30 | -0.5773502692 | -0.5773502692 |  -1.732050808 |  -1.732050808
+  -20 | -0.3639702343 | -0.3639702343 |  -2.747477419 |  -2.747477419
+  -10 | -0.1763269807 | -0.1763269807 |   -5.67128182 |   -5.67128182
+    0 |             0 |             0 |      Infinity |      Infinity
+   10 |  0.1763269807 |  0.1763269807 |    5.67128182 |    5.67128182
+   20 |  0.3639702343 |  0.3639702343 |   2.747477419 |   2.747477419
+   30 |  0.5773502692 |  0.5773502692 |   1.732050808 |   1.732050808
+   40 |  0.8390996312 |  0.8390996312 |   1.191753593 |   1.191753593
+   50 |   1.191753593 |   1.191753593 |  0.8390996312 |  0.8390996312
+   60 |   1.732050808 |   1.732050808 |  0.5773502692 |  0.5773502692
+   70 |   2.747477419 |   2.747477419 |  0.3639702343 |  0.3639702343
+   80 |    5.67128182 |    5.67128182 |  0.1763269807 |  0.1763269807
+  100 |   -5.67128182 |   -5.67128182 | -0.1763269807 | -0.1763269807
+  110 |  -2.747477419 |  -2.747477419 | -0.3639702343 | -0.3639702343
+  120 |  -1.732050808 |  -1.732050808 | -0.5773502692 | -0.5773502692
+  130 |  -1.191753593 |  -1.191753593 | -0.8390996312 | -0.8390996312
+  140 | -0.8390996312 | -0.8390996312 |  -1.191753593 |  -1.191753593
+  150 | -0.5773502692 | -0.5773502692 |  -1.732050808 |  -1.732050808
+  160 | -0.3639702343 | -0.3639702343 |  -2.747477419 |  -2.747477419
+  170 | -0.1763269807 | -0.1763269807 |   -5.67128182 |   -5.67128182
+  190 |  0.1763269807 |  0.1763269807 |    5.67128182 |    5.67128182
+  200 |  0.3639702343 |  0.3639702343 |   2.747477419 |   2.747477419
+  210 |  0.5773502692 |  0.5773502692 |   1.732050808 |   1.732050808
+  220 |  0.8390996312 |  0.8390996312 |   1.191753593 |   1.191753593
+  230 |   1.191753593 |   1.191753593 |  0.8390996312 |  0.8390996312
+  240 |   1.732050808 |   1.732050808 |  0.5773502692 |  0.5773502692
+  250 |   2.747477419 |   2.747477419 |  0.3639702343 |  0.3639702343
+  260 |    5.67128182 |    5.67128182 |  0.1763269807 |  0.1763269807
+  280 |   -5.67128182 |   -5.67128182 | -0.1763269807 | -0.1763269807
+  290 |  -2.747477419 |  -2.747477419 | -0.3639702343 | -0.3639702343
+  300 |  -1.732050808 |  -1.732050808 | -0.5773502692 | -0.5773502692
+  310 |  -1.191753593 |  -1.191753593 | -0.8390996312 | -0.8390996312
+  320 | -0.8390996312 | -0.8390996312 |  -1.191753593 |  -1.191753593
+  330 | -0.5773502692 | -0.5773502692 |  -1.732050808 |  -1.732050808
+  340 | -0.3639702343 | -0.3639702343 |  -2.747477419 |  -2.747477419
+  350 | -0.1763269807 | -0.1763269807 |   -5.67128182 |   -5.67128182
+(65 rows)
+
+-- test exactness
+SELECT x,
+       CASE WHEN tand(x) IN (-1,0,1) THEN tand(x) END AS tand,
+       CASE WHEN cotd(x) IN (-1,0,1) THEN cotd(x) END AS cotd
+FROM generate_series(0, 360, 45) AS t(x);
+  x  | tand | cotd 
+-----+------+------
+   0 |    0 |     
+  45 |    1 |    1
+  90 |      |    0
+ 135 |   -1 |   -1
+ 180 |   -0 |     
+ 225 |    1 |    1
+ 270 |      |   -0
+ 315 |   -1 |   -1
+ 360 |    0 |     
+(9 rows)
+
+-- arcsine and arccosine
+SELECT x,
+       degrees(asin(x)) AS degrees_asin, asind(x),
+       degrees(acos(x)) AS degrees_acos, acosd(x)
+FROM generate_series(-1.0, 1.0, 0.1) AS t(x);
+  x   | degrees_asin |    asind     | degrees_acos |    acosd    
+------+--------------+--------------+--------------+-------------
+ -1.0 |          -90 |          -90 |          180 |         180
+ -0.9 | -64.15806724 | -64.15806724 |  154.1580672 | 154.1580672
+ -0.8 | -53.13010235 | -53.13010235 |  143.1301024 | 143.1301024
+ -0.7 |   -44.427004 |   -44.427004 |   134.427004 |  134.427004
+ -0.6 | -36.86989765 | -36.86989765 |  126.8698976 | 126.8698976
+ -0.5 |          -30 |          -30 |          120 |         120
+ -0.4 | -23.57817848 | -23.57817848 |  113.5781785 | 113.5781785
+ -0.3 | -17.45760312 | -17.45760312 |  107.4576031 | 107.4576031
+ -0.2 | -11.53695903 | -11.53695903 |   101.536959 |  101.536959
+ -0.1 | -5.739170477 | -5.739170477 |  95.73917048 | 95.73917048
+  0.0 |            0 |            0 |           90 |          90
+  0.1 |  5.739170477 |  5.739170477 |  84.26082952 | 84.26082952
+  0.2 |  11.53695903 |  11.53695903 |  78.46304097 | 78.46304097
+  0.3 |  17.45760312 |  17.45760312 |  72.54239688 | 72.54239688
+  0.4 |  23.57817848 |  23.57817848 |  66.42182152 | 66.42182152
+  0.5 |           30 |           30 |           60 |          60
+  0.6 |  36.86989765 |  36.86989765 |  53.13010235 | 53.13010235
+  0.7 |    44.427004 |    44.427004 |    45.572996 |   45.572996
+  0.8 |  53.13010235 |  53.13010235 |  36.86989765 | 36.86989765
+  0.9 |  64.15806724 |  64.15806724 |  25.84193276 | 25.84193276
+  1.0 |           90 |           90 |            0 |           0
+(21 rows)
+
+-- arctangent
+SELECT x, degrees(atan(x)) AS degrees_atan, atand(x)
+FROM (SELECT '-Infinity'::float8
+      UNION ALL
+      SELECT generate_series(-10, -2)
+      UNION ALL
+      SELECT generate_series(-1.0, 1.0, 0.1)
+      UNION ALL
+      SELECT generate_series(2, 10)
+      UNION ALL
+      SELECT 'Infinity'::float8
+      UNION ALL
+      SELECT 'NaN'::float8) AS t(x);
+     x     | degrees_atan |    atand     
+-----------+--------------+--------------
+ -Infinity |          -90 |          -90
+       -10 | -84.28940686 | -84.28940686
+        -9 | -83.65980825 | -83.65980825
+        -8 | -82.87498365 | -82.87498365
+        -7 | -81.86989765 | -81.86989765
+        -6 | -80.53767779 | -80.53767779
+        -5 | -78.69006753 | -78.69006753
+        -4 | -75.96375653 | -75.96375653
+        -3 | -71.56505118 | -71.56505118
+        -2 | -63.43494882 | -63.43494882
+        -1 |          -45 |          -45
+      -0.9 |  -41.9872125 |  -41.9872125
+      -0.8 | -38.65980825 | -38.65980825
+      -0.7 |  -34.9920202 |  -34.9920202
+      -0.6 | -30.96375653 | -30.96375653
+      -0.5 | -26.56505118 | -26.56505118
+      -0.4 | -21.80140949 | -21.80140949
+      -0.3 | -16.69924423 | -16.69924423
+      -0.2 | -11.30993247 | -11.30993247
+      -0.1 | -5.710593137 | -5.710593137
+         0 |            0 |            0
+       0.1 |  5.710593137 |  5.710593137
+       0.2 |  11.30993247 |  11.30993247
+       0.3 |  16.69924423 |  16.69924423
+       0.4 |  21.80140949 |  21.80140949
+       0.5 |  26.56505118 |  26.56505118
+       0.6 |  30.96375653 |  30.96375653
+       0.7 |   34.9920202 |   34.9920202
+       0.8 |  38.65980825 |  38.65980825
+       0.9 |   41.9872125 |   41.9872125
+         1 |           45 |           45
+         2 |  63.43494882 |  63.43494882
+         3 |  71.56505118 |  71.56505118
+         4 |  75.96375653 |  75.96375653
+         5 |  78.69006753 |  78.69006753
+         6 |  80.53767779 |  80.53767779
+         7 |  81.86989765 |  81.86989765
+         8 |  82.87498365 |  82.87498365
+         9 |  83.65980825 |  83.65980825
+        10 |  84.28940686 |  84.28940686
+  Infinity |           90 |           90
+       NaN |          NaN |          NaN
+(42 rows)
+
+-- 4 quadrant arctangent
+SELECT x, y, degrees(atan2(y, x)) AS degrees_atan2, atan2d(y, x)
+FROM (SELECT 10*cosd(a), 10*sind(a)
+      FROM generate_series(0, 360, 10) AS t(a)) AS t(x,y);
+      x       |      y       | degrees_atan2 | atan2d 
+--------------+--------------+---------------+--------
+           10 |            0 |             0 |      0
+   9.84807753 |  1.736481777 |            10 |     10
+  9.396926208 |  3.420201433 |            20 |     20
+  8.660254038 |            5 |            30 |     30
+  7.660444431 |  6.427876097 |            40 |     40
+  6.427876097 |  7.660444431 |            50 |     50
+            5 |  8.660254038 |            60 |     60
+  3.420201433 |  9.396926208 |            70 |     70
+  1.736481777 |   9.84807753 |            80 |     80
+            0 |           10 |            90 |     90
+ -1.736481777 |   9.84807753 |           100 |    100
+ -3.420201433 |  9.396926208 |           110 |    110
+           -5 |  8.660254038 |           120 |    120
+ -6.427876097 |  7.660444431 |           130 |    130
+ -7.660444431 |  6.427876097 |           140 |    140
+ -8.660254038 |            5 |           150 |    150
+ -9.396926208 |  3.420201433 |           160 |    160
+  -9.84807753 |  1.736481777 |           170 |    170
+          -10 |            0 |           180 |    180
+  -9.84807753 | -1.736481777 |          -170 |   -170
+ -9.396926208 | -3.420201433 |          -160 |   -160
+ -8.660254038 |           -5 |          -150 |   -150
+ -7.660444431 | -6.427876097 |          -140 |   -140
+ -6.427876097 | -7.660444431 |          -130 |   -130
+           -5 | -8.660254038 |          -120 |   -120
+ -3.420201433 | -9.396926208 |          -110 |   -110
+ -1.736481777 |  -9.84807753 |          -100 |   -100
+            0 |          -10 |           -90 |    -90
+  1.736481777 |  -9.84807753 |           -80 |    -80
+  3.420201433 | -9.396926208 |           -70 |    -70
+            5 | -8.660254038 |           -60 |    -60
+  6.427876097 | -7.660444431 |           -50 |    -50
+  7.660444431 | -6.427876097 |           -40 |    -40
+  8.660254038 |           -5 |           -30 |    -30
+  9.396926208 | -3.420201433 |           -20 |    -20
+   9.84807753 | -1.736481777 |           -10 |    -10
+           10 |            0 |             0 |      0
+(37 rows)
+
diff --git a/src/test/regress/sql/float8.sql b/src/test/regress/sql/float8.sql
index 92a574a..49363d0 100644
--- a/src/test/regress/sql/float8.sql
+++ b/src/test/regress/sql/float8.sql
@@ -167,3 +167,58 @@ 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 the trigonometric functions.
+-- Use a lower precision to eliminate variations between platforms.
+SET extra_float_digits TO -5;
+
+-- sine and cosine
+SELECT x,
+       sin(radians(x)) AS sin_radians, sind(x),
+       cos(radians(x)) AS cos_radians, cosd(x)
+FROM generate_series(-360, 360, 10) AS t(x)
+WHERE x = 0 OR x % 90 != 0; -- skip tests that are very platform-dependent
+
+-- test exactness
+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
+FROM generate_series(0, 360, 30) AS t(x);
+
+-- tangent and cotangent
+SELECT x,
+       tan(radians(x)) AS tan_radians, tand(x),
+       cot(radians(x)) AS cot_radians, cotd(x)
+FROM generate_series(-360, 360, 10) AS t(x)
+WHERE x = 0 OR x % 90 != 0; -- skip tests that are very platform-dependent
+
+-- test exactness
+SELECT x,
+       CASE WHEN tand(x) IN (-1,0,1) THEN tand(x) END AS tand,
+       CASE WHEN cotd(x) IN (-1,0,1) THEN cotd(x) END AS cotd
+FROM generate_series(0, 360, 45) AS t(x);
+
+-- arcsine and arccosine
+SELECT x,
+       degrees(asin(x)) AS degrees_asin, asind(x),
+       degrees(acos(x)) AS degrees_acos, acosd(x)
+FROM generate_series(-1.0, 1.0, 0.1) AS t(x);
+
+-- arctangent
+SELECT x, degrees(atan(x)) AS degrees_atan, atand(x)
+FROM (SELECT '-Infinity'::float8
+      UNION ALL
+      SELECT generate_series(-10, -2)
+      UNION ALL
+      SELECT generate_series(-1.0, 1.0, 0.1)
+      UNION ALL
+      SELECT generate_series(2, 10)
+      UNION ALL
+      SELECT 'Infinity'::float8
+      UNION ALL
+      SELECT 'NaN'::float8) AS t(x);
+
+-- 4 quadrant arctangent
+SELECT x, y, degrees(atan2(y, x)) AS degrees_atan2, atan2d(y, x)
+FROM (SELECT 10*cosd(a), 10*sind(a)
+      FROM generate_series(0, 360, 10) AS t(a)) AS t(x,y);
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to