Commit e1bd684a authored by Tom Lane's avatar Tom Lane

Add trigonometric functions that work in degrees.

The implementations go to some lengths to deliver exact results for values
where an exact result can be expected, such as sind(30) = 0.5 exactly.

Dean Rasheed, reviewed by Michael Paquier
parent fd5200c3
......@@ -1006,20 +1006,19 @@
Finally, <xref linkend="functions-math-trig-table"> shows the
available trigonometric functions. All trigonometric functions
take arguments and return values of type <type>double
precision</type>. Trigonometric functions arguments are expressed
in radians. Inverse functions return values are expressed in
radians. See unit transformation functions
<literal><function>radians()</function></literal> and
<literal><function>degrees()</function></literal> above.
precision</type>. Each of the trigonometric functions comes in
two variants, one that measures angles in radians and one that
measures angles in degrees.
</para>
<table id="functions-math-trig-table">
<title>Trigonometric Functions</title>
<tgroup cols="2">
<tgroup cols="3">
<thead>
<row>
<entry>Function</entry>
<entry>Function (radians)</entry>
<entry>Function (degrees)</entry>
<entry>Description</entry>
</row>
</thead>
......@@ -1031,6 +1030,11 @@
<primary>acos</primary>
</indexterm><literal><function>acos(<replaceable>x</replaceable>)</function></literal>
</entry>
<entry>
<indexterm>
<primary>acosd</primary>
</indexterm><literal><function>acosd(<replaceable>x</replaceable>)</function></literal>
</entry>
<entry>inverse cosine</entry>
</row>
......@@ -1041,6 +1045,12 @@
</indexterm>
<literal><function>asin(<replaceable>x</replaceable>)</function></literal>
</entry>
<entry>
<indexterm>
<primary>asind</primary>
</indexterm>
<literal><function>asind(<replaceable>x</replaceable>)</function></literal>
</entry>
<entry>inverse sine</entry>
</row>
......@@ -1051,6 +1061,12 @@
</indexterm>
<literal><function>atan(<replaceable>x</replaceable>)</function></literal>
</entry>
<entry>
<indexterm>
<primary>atand</primary>
</indexterm>
<literal><function>atand(<replaceable>x</replaceable>)</function></literal>
</entry>
<entry>inverse tangent</entry>
</row>
......@@ -1062,6 +1078,13 @@
<literal><function>atan2(<replaceable>y</replaceable>,
<replaceable>x</replaceable>)</function></literal>
</entry>
<entry>
<indexterm>
<primary>atan2d</primary>
</indexterm>
<literal><function>atan2d(<replaceable>y</replaceable>,
<replaceable>x</replaceable>)</function></literal>
</entry>
<entry>inverse tangent of
<literal><replaceable>y</replaceable>/<replaceable>x</replaceable></literal></entry>
</row>
......@@ -1073,6 +1096,12 @@
</indexterm>
<literal><function>cos(<replaceable>x</replaceable>)</function></literal>
</entry>
<entry>
<indexterm>
<primary>cosd</primary>
</indexterm>
<literal><function>cosd(<replaceable>x</replaceable>)</function></literal>
</entry>
<entry>cosine</entry>
</row>
......@@ -1083,6 +1112,12 @@
</indexterm>
<literal><function>cot(<replaceable>x</replaceable>)</function></literal>
</entry>
<entry>
<indexterm>
<primary>cotd</primary>
</indexterm>
<literal><function>cotd(<replaceable>x</replaceable>)</function></literal>
</entry>
<entry>cotangent</entry>
</row>
......@@ -1093,6 +1128,12 @@
</indexterm>
<literal><function>sin(<replaceable>x</replaceable>)</function></literal>
</entry>
<entry>
<indexterm>
<primary>sind</primary>
</indexterm>
<literal><function>sind(<replaceable>x</replaceable>)</function></literal>
</entry>
<entry>sine</entry>
</row>
......@@ -1103,12 +1144,29 @@
</indexterm>
<literal><function>tan(<replaceable>x</replaceable>)</function></literal>
</entry>
<entry>
<indexterm>
<primary>tand</primary>
</indexterm>
<literal><function>tand(<replaceable>x</replaceable>)</function></literal>
</entry>
<entry>tangent</entry>
</row>
</tbody>
</tgroup>
</table>
<note>
<para>
Another way to work with angles measured in degrees is to use the unit
transformation functions <literal><function>radians()</function></literal>
and <literal><function>degrees()</function></literal> shown earlier.
However, using the degree-based trigonometric functions is preferred,
as that way avoids roundoff error for special cases such
as <literal>sind(30)</>.
</para>
</note>
</sect1>
......
This diff is collapsed.
......@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 201601201
#define CATALOG_VERSION_NO 201601221
#endif
......@@ -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_ ));
......
......@@ -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);
......
......@@ -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)
......@@ -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)
......@@ -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)
......@@ -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)
......@@ -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);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment