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
This commit is contained in:
Tom Lane 2016-01-22 15:46:22 -05:00
parent fd5200c3dc
commit e1bd684a34
10 changed files with 860 additions and 9 deletions

View File

@ -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>

View 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
*/

View File

@ -53,6 +53,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 201601201
#define CATALOG_VERSION_NO 201601221
#endif

View 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_ ));

View 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);

View 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)

View 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)

View 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)

View 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)

View 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);