On 30 November 2015 at 14:11, Tom Lane <t...@sss.pgh.pa.us> wrote:
> FWIW, I think that probably the best course of action is to go ahead
> and install POSIX-compliant error checking in the existing functions
> too.  POSIX:2008 is quite clear about this:
>
> : An application wishing to check for error situations should set errno to
> : zero and call feclearexcept(FE_ALL_EXCEPT) before calling these
> : functions. On return, if errno is non-zero or fetestexcept(FE_INVALID |
> : FE_DIVBYZERO | FE_OVERFLOW | FE_UNDERFLOW) is non-zero, an error has
> : occurred.
>

Looking at this again, I think it makes sense to make the Inf/NaN
handling of these functions platform-independent. POSIX.1-2008 is
pretty explicit about how they ought to behave, which is different
from the current behaviour on either Linux or Windows:

sin(Inf):
  POSIX: domain error
  Linux: NaN
  Windows: ERROR:  input is out of range

asin(Inf):
  POSIX: domain error
  Linux: ERROR:  input is out of range
  Windows: ERROR:  input is out of range

sin(NaN):
  POSIX: NaN
  Linux: NaN
  Windows: ERROR:  input is out of range

asin(NaN):
  POSIX: NaN
  Linux: NaN
  Windows: ERROR:  input is out of range

I tried using feclearexcept + fetestexcept as recommended by
POSIX.1-2008, and that does indeed make the above examples compliant
on my linux box. Unfortunately it introduces new errors -- asin starts
generating FE_UNDERFLOW errors for numbers that are really not that
small, such as asin(1e-9), although the function still returns the
correct answer. A bigger problem though is that these are C99
functions and so won't necessarily be available on all supported
platforms.

So I think that a simpler answer is to just to add explicit tests for
these inputs and avoid relying on errno, at least for the inverse
functions, which have pretty clear constraints on their allowed
inputs. For the forward functions, I'm not sure if there are some
platforms on which large but finite inputs might generate errors, so I
think it's safest to keep the existing errno checks as well just in
case.

Attached are patches for this and the new functions in degrees, now
with POSIX compatible Inf/NaN handling.

Regards,
Dean
diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c
new file mode 100644
index 8f34209..4ce0129
*** a/src/backend/utils/adt/float.c
--- b/src/backend/utils/adt/float.c
*************** dacos(PG_FUNCTION_ARGS)
*** 1524,1541 ****
  	float8		arg1 = PG_GETARG_FLOAT8(0);
  	float8		result;
  
  	/*
! 	 * We use errno here because the trigonometric functions are cyclic and
! 	 * hard to check for underflow.
  	 */
! 	errno = 0;
! 	result = acos(arg1);
! 	if (errno != 0)
  		ereport(ERROR,
  				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
  				 errmsg("input is out of range")));
  
! 	CHECKFLOATVAL(result, isinf(arg1), true);
  	PG_RETURN_FLOAT8(result);
  }
  
--- 1524,1546 ----
  	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, Pi], 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")));
  
! 	result = acos(arg1);
! 
! 	CHECKFLOATVAL(result, false, true);
  	PG_RETURN_FLOAT8(result);
  }
  
*************** dasin(PG_FUNCTION_ARGS)
*** 1549,1562 ****
  	float8		arg1 = PG_GETARG_FLOAT8(0);
  	float8		result;
  
! 	errno = 0;
! 	result = asin(arg1);
! 	if (errno != 0)
  		ereport(ERROR,
  				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
  				 errmsg("input is out of range")));
  
! 	CHECKFLOATVAL(result, isinf(arg1), true);
  	PG_RETURN_FLOAT8(result);
  }
  
--- 1554,1576 ----
  	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 [-Pi/2, Pi/2], 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")));
  
! 	result = asin(arg1);
! 
! 	CHECKFLOATVAL(result, false, true);
  	PG_RETURN_FLOAT8(result);
  }
  
*************** datan(PG_FUNCTION_ARGS)
*** 1570,1583 ****
  	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")));
  
! 	CHECKFLOATVAL(result, isinf(arg1), true);
  	PG_RETURN_FLOAT8(result);
  }
  
--- 1584,1601 ----
  	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 [-Pi/2, Pi/2], so the result should always be
! 	 * finite, even if the input is infinite.
! 	 */
  	result = atan(arg1);
  
! 	CHECKFLOATVAL(result, false, true);
  	PG_RETURN_FLOAT8(result);
  }
  
*************** datan2(PG_FUNCTION_ARGS)
*** 1592,1605 ****
  	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")));
  
! 	CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2), true);
  	PG_RETURN_FLOAT8(result);
  }
  
--- 1610,1626 ----
  	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());
! 
! 	/*
! 	 * atan2 maps all inputs to values in the range [-Pi, Pi], so the result
! 	 * should always be finite, even if the inputs are infinite.
! 	 */
  	result = atan2(arg1, arg2);
  
! 	CHECKFLOATVAL(result, false, true);
  	PG_RETURN_FLOAT8(result);
  }
  
*************** dcos(PG_FUNCTION_ARGS)
*** 1613,1626 ****
  	float8		arg1 = PG_GETARG_FLOAT8(0);
  	float8		result;
  
  	errno = 0;
  	result = cos(arg1);
! 	if (errno != 0)
  		ereport(ERROR,
  				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
  				 errmsg("input is out of range")));
  
! 	CHECKFLOATVAL(result, isinf(arg1), true);
  	PG_RETURN_FLOAT8(result);
  }
  
--- 1634,1660 ----
  	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 trigonometric functions are periodic and so should work for all
+ 	 * finite inputs, but it's possible that some platforms may have a more
+ 	 * limited range, so we use errno to detect other out-of-range inputs.
+ 	 *
+ 	 * For infinite inputs, POSIX specifies that the trigonometric functions
+ 	 * should return errno == EDOM, but not all platforms conform to that, so
+ 	 * we explicitly test for infinite inputs if errno is not set.
+ 	 */
  	errno = 0;
  	result = cos(arg1);
! 	if (errno != 0 || isinf(arg1))
  		ereport(ERROR,
  				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
  				 errmsg("input is out of range")));
  
! 	CHECKFLOATVAL(result, false /* result is always finite */ , true);
  	PG_RETURN_FLOAT8(result);
  }
  
*************** dcot(PG_FUNCTION_ARGS)
*** 1634,1648 ****
  	float8		arg1 = PG_GETARG_FLOAT8(0);
  	float8		result;
  
  	errno = 0;
  	result = tan(arg1);
! 	if (errno != 0)
  		ereport(ERROR,
  				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
  				 errmsg("input is out of range")));
  
  	result = 1.0 / result;
! 	CHECKFLOATVAL(result, true /* cotan(pi/2) == inf */ , true);
  	PG_RETURN_FLOAT8(result);
  }
  
--- 1668,1687 ----
  	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());
+ 
+ 	/* Be sure to throw an error if the input is infinite --- see dcos */
  	errno = 0;
  	result = tan(arg1);
! 	if (errno != 0 || isinf(arg1))
  		ereport(ERROR,
  				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
  				 errmsg("input is out of range")));
  
  	result = 1.0 / result;
! 	CHECKFLOATVAL(result, true /* cot(0) == Inf */ , true);
  	PG_RETURN_FLOAT8(result);
  }
  
*************** dsin(PG_FUNCTION_ARGS)
*** 1656,1669 ****
  	float8		arg1 = PG_GETARG_FLOAT8(0);
  	float8		result;
  
  	errno = 0;
  	result = sin(arg1);
! 	if (errno != 0)
  		ereport(ERROR,
  				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
  				 errmsg("input is out of range")));
  
! 	CHECKFLOATVAL(result, isinf(arg1), true);
  	PG_RETURN_FLOAT8(result);
  }
  
--- 1695,1713 ----
  	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());
+ 
+ 	/* Be sure to throw an error if the input is infinite --- see dcos */
  	errno = 0;
  	result = sin(arg1);
! 	if (errno != 0 || isinf(arg1))
  		ereport(ERROR,
  				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
  				 errmsg("input is out of range")));
  
! 	CHECKFLOATVAL(result, false /* result is always finite */ , true);
  	PG_RETURN_FLOAT8(result);
  }
  
*************** dtan(PG_FUNCTION_ARGS)
*** 1677,1685 ****
  	float8		arg1 = PG_GETARG_FLOAT8(0);
  	float8		result;
  
  	errno = 0;
  	result = tan(arg1);
! 	if (errno != 0)
  		ereport(ERROR,
  				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
  				 errmsg("input is out of range")));
--- 1721,1734 ----
  	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());
+ 
+ 	/* Be sure to throw an error if the input is infinite --- see dcos */
  	errno = 0;
  	result = tan(arg1);
! 	if (errno != 0 || isinf(arg1))
  		ereport(ERROR,
  				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
  				 errmsg("input is out of range")));
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
new file mode 100644
index 0af01d9..d9a218c
*** a/doc/src/sgml/func.sgml
--- b/doc/src/sgml/func.sgml
***************
*** 1006,1014 ****
     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.
    </para>
--- 1006,1014 ----
     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>.  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>
***************
*** 1016,1025 ****
     <table id="functions-math-trig-table">
      <title>Trigonometric Functions</title>
  
!     <tgroup cols="2">
       <thead>
        <row>
!        <entry>Function</entry>
         <entry>Description</entry>
        </row>
       </thead>
--- 1016,1026 ----
     <table id="functions-math-trig-table">
      <title>Trigonometric Functions</title>
  
!     <tgroup cols="3">
       <thead>
        <row>
!        <entry>Function (radians)</entry>
!        <entry>Function (degrees)</entry>
         <entry>Description</entry>
        </row>
       </thead>
***************
*** 1031,1036 ****
--- 1032,1042 ----
           <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,1046 ****
--- 1047,1058 ----
          </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,1056 ****
--- 1063,1074 ----
          </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,1067 ****
--- 1080,1092 ----
          <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,1078 ****
--- 1098,1109 ----
          </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,1088 ****
--- 1114,1125 ----
          </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,1098 ****
--- 1130,1141 ----
          </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,1108 ****
--- 1146,1157 ----
          </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
new file mode 100644
index 8f34209..9d48406
*** a/src/backend/utils/adt/float.c
--- b/src/backend/utils/adt/float.c
*************** dtan(PG_FUNCTION_ARGS)
*** 1690,1695 ****
--- 1690,2130 ----
  
  
  /*
+  *		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 arctan2 of arg1 (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 ( 2.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 /* result is always finite */ , 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 /* result is always finite */ , 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/pg_proc.h b/src/include/catalog/pg_proc.h
new file mode 100644
index f58672e..3c67b91
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
*************** DATA(insert OID = 1606 (  tan				PGNSP P
*** 1894,1899 ****
--- 1894,1917 ----
  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 = 3318 (  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 = 3319 (  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 = 3320 (  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 = 3321 (  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 = 3322 (  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 = 3323 (  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 = 3324 (  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 = 3325 (  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
new file mode 100644
index b35d206..75f1d20
*** a/src/include/utils/builtins.h
--- b/src/include/utils/builtins.h
*************** extern Datum dcos(PG_FUNCTION_ARGS);
*** 407,412 ****
--- 407,420 ----
  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
new file mode 100644
index 2dd648d..6891ee0
*** a/src/test/regress/expected/float8-exp-three-digits-win32.out
--- b/src/test/regress/expected/float8-exp-three-digits-win32.out
*************** SELECT '' AS five, * FROM FLOAT8_TBL;
*** 444,446 ****
--- 444,523 ----
        | -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
new file mode 100644
index 5da7433..e158e70
*** a/src/test/regress/expected/float8-small-is-zero.out
--- b/src/test/regress/expected/float8-small-is-zero.out
*************** SELECT '' AS five, * FROM FLOAT8_TBL;
*** 442,444 ****
--- 442,521 ----
        | -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
new file mode 100644
index 530842e..42e50a0
*** a/src/test/regress/expected/float8-small-is-zero_1.out
--- b/src/test/regress/expected/float8-small-is-zero_1.out
*************** SELECT '' AS five, * FROM FLOAT8_TBL;
*** 442,444 ****
--- 442,521 ----
        | -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
new file mode 100644
index 6221538..b77b34f
*** a/src/test/regress/expected/float8.out
--- b/src/test/regress/expected/float8.out
*************** SELECT '' AS five, * FROM FLOAT8_TBL;
*** 444,446 ****
--- 444,523 ----
        | -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
new file mode 100644
index 92a574a..45a484b
*** a/src/test/regress/sql/float8.sql
--- b/src/test/regress/sql/float8.sql
*************** INSERT INTO FLOAT8_TBL(f1) VALUES ('-1.2
*** 167,169 ****
--- 167,193 ----
  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);
-- 
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