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:
parent
fd5200c3dc
commit
e1bd684a34
@ -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>
|
||||
|
||||
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user