]> granicus.if.org Git - postgresql/commitdiff
Add trigonometric functions that work in degrees.
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 22 Jan 2016 20:46:22 +0000 (15:46 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 22 Jan 2016 20:46:22 +0000 (15:46 -0500)
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

doc/src/sgml/func.sgml
src/backend/utils/adt/float.c
src/include/catalog/catversion.h
src/include/catalog/pg_proc.h
src/include/utils/builtins.h
src/test/regress/expected/float8-exp-three-digits-win32.out
src/test/regress/expected/float8-small-is-zero.out
src/test/regress/expected/float8-small-is-zero_1.out
src/test/regress/expected/float8.out
src/test/regress/sql/float8.sql

index 4d2b88fafd3ee8b0c6e2a405a7d8052e6fb2dd96..9c143b2a634a7eaba8672c38dbf9ad45cfa90d66 100644 (file)
    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>
          <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>
 
         </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>
 
         </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>
 
         <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>
         </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>
 
         </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>
 
         </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>
 
         </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>
 
 
index a3a989ed28947e702a5047b9f8101fba8519068e..51e996ceb2a68a915f55331b650a405601cd6665 100644 (file)
@@ -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)
@@ -1744,6 +1744,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
  */
index 58709f56ac50e0984de6f4062ba8d17d1aa3c333..894a619f94bd5499303752091f871574cf26ba9b 100644 (file)
@@ -53,6 +53,6 @@
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     201601201
+#define CATALOG_VERSION_NO     201601221
 
 #endif
index 244aa4d016a1a7f7633be9233675ce34fa9d352e..79e92ffa785732e29b8dbee0c7b8386c3cd56aa5 100644 (file)
@@ -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_ ));
index 3c134a3aa9688cef8970d75a59ff6b2c09286ddf..c2e529fc6f6d6f444777406bf5a5b85295315532 100644 (file)
@@ -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);
index 2dd648d6b9221058cdfa1c2702b6d4a84d1c1ce5..6891ee0b4aafceaf5b4188003daad607aba81aa6 100644 (file)
@@ -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)
+
index 5da743374c9174112fd7bfcaff2b17f25bd1d3a3..e158e7093a238cfe7f37afaee04518efc10933f6 100644 (file)
@@ -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)
+
index 530842e10233f096b22f371d2dd597e0fd376743..42e50a04648997f6d3853621888dd7eddce0f75e 100644 (file)
@@ -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)
+
index 6221538af5c9b8f8adcbd3755eda467e94cc254d..b77b34f2b3099387579cc74b76597944be032451 100644 (file)
@@ -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)
+
index 92a574ab7bff24aa688d265cee98b4631cc19bc5..45a484b8b95b90a37987d56c580a05a87ad1333c 100644 (file)
@@ -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);