On 11 November 2015 at 11:45, Dean Rasheed <dean.a.rash...@gmail.com> wrote: > Thanks for testing. I'll post an updated patch sometime soon. >
I finally got round to looking at this again, and here is an updated patch. I've reverted the changes to the CHECKFLOATVAL() checks in the existing functions, so that can be a separate discussion. As for the checks in the new functions added by this patch, I've left them as they were on the grounds that the checks are taking place after argument reduction, so the arguments will not be infinite at that point (and besides, I think the checks are correct as written anyway). I've reduced the regression tests down to checks of the cases where the results should be exact, so they should now be platform-independent. Many of the original tests were useful during development to ensure that sane-looking answers were being returned, but they didn't really add anything as regression tests, other than extra complication due to platform variations. Regards, Dean
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml new file mode 100644 index 60b9a09..3716210 *** a/doc/src/sgml/func.sgml --- b/doc/src/sgml/func.sgml *************** *** 993,1001 **** Finally, <xref linkend="functions-math-trig-table"> shows the available trigonometric functions. All trigonometric functions take arguments and return values of type <type>double ! precision</type>. Trigonometric functions arguments are expressed ! in radians. Inverse functions return values are expressed in ! radians. See unit transformation functions <literal><function>radians()</function></literal> and <literal><function>degrees()</function></literal> above. </para> --- 993,1001 ---- Finally, <xref linkend="functions-math-trig-table"> shows the available trigonometric functions. All trigonometric functions take arguments and return values of type <type>double ! precision</type>. Each of the trigonometric functions comes in ! two varieties, one which works in radians and one which works in ! degrees. See unit transformation functions <literal><function>radians()</function></literal> and <literal><function>degrees()</function></literal> above. </para> *************** *** 1003,1012 **** <table id="functions-math-trig-table"> <title>Trigonometric Functions</title> ! <tgroup cols="2"> <thead> <row> ! <entry>Function</entry> <entry>Description</entry> </row> </thead> --- 1003,1013 ---- <table id="functions-math-trig-table"> <title>Trigonometric Functions</title> ! <tgroup cols="3"> <thead> <row> ! <entry>Function (radians)</entry> ! <entry>Function (degrees)</entry> <entry>Description</entry> </row> </thead> *************** *** 1018,1023 **** --- 1019,1029 ---- <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> *************** *** 1028,1033 **** --- 1034,1045 ---- </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> *************** *** 1038,1043 **** --- 1050,1061 ---- </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> *************** *** 1049,1054 **** --- 1067,1079 ---- <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> *************** *** 1060,1065 **** --- 1085,1096 ---- </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> *************** *** 1070,1075 **** --- 1101,1112 ---- </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> *************** *** 1080,1085 **** --- 1117,1128 ---- </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> *************** *** 1090,1095 **** --- 1133,1144 ---- </indexterm> <literal><function>tan(<replaceable>x</replaceable>)</function></literal> </entry> + <entry> + <indexterm> + <primary>tand</primary> + </indexterm> + <literal><function>tand(<replaceable>x</replaceable>)</function></literal> + </entry> <entry>tangent</entry> </row> </tbody> diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c new file mode 100644 index 4e927d8..4a6fdae *** a/src/backend/utils/adt/float.c --- b/src/backend/utils/adt/float.c *************** dcot(PG_FUNCTION_ARGS) *** 1642,1648 **** errmsg("input is out of range"))); result = 1.0 / result; ! CHECKFLOATVAL(result, true /* cotan(pi/2) == inf */ , true); PG_RETURN_FLOAT8(result); } --- 1642,1648 ---- errmsg("input is out of range"))); result = 1.0 / result; ! CHECKFLOATVAL(result, true /* cot(0) == Inf */ , true); PG_RETURN_FLOAT8(result); } *************** dtan(PG_FUNCTION_ARGS) *** 1688,1693 **** --- 1688,2056 ---- PG_RETURN_FLOAT8(result); } + + /* + * asind_q1 - returns the inverse sine of x in degrees, where x is + * assumed to be in the range [0, 1] and the result is + * an angle in the first quadrant (0 to 90 degrees). + * + * In this quadrant there are 3 special case inputs (0, 0.5 and 1) for which + * this function will return exact values (0, 30 and 90 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, where x + * is assumed to be in the range [0, 1] and the result + * is an angle in the first quadrant (0 to 90 degrees). + * + * In this quadrant there are 3 special case inputs (0, 0.5 and 1) for which + * this function will return exact values (0, 60 and 90 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; + + if (isinf(arg1) || arg1 < -1.0 || arg1 > 1.0) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("input is out of range"))); + + if (isnan(arg1)) + result = arg1; + else 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; + + if (isinf(arg1) || arg1 < -1.0 || arg1 > 1.0) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("input is out of range"))); + + if (isnan(arg1)) + result = arg1; + else 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; + + errno = 0; + result = atan(arg1); + if (errno != 0) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("input is out of range"))); + + result = (result / atan(1.0)) * 45.0; + CHECKFLOATVAL(result, false, true); + PG_RETURN_FLOAT8(result); + } + + + /* + * atan2d - returns the arctan2 of arg1 (degrees) + */ + Datum + datan2d(PG_FUNCTION_ARGS) + { + float8 arg1 = PG_GETARG_FLOAT8(0); + float8 arg2 = PG_GETARG_FLOAT8(1); + float8 result; + + errno = 0; + result = atan2(arg1, arg2); + if (errno != 0) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("input is out of range"))); + + result = (result / atan(1.0)) * 45.0; + CHECKFLOATVAL(result, false, true); + PG_RETURN_FLOAT8(result); + } + + + /* + * sind_0_to_30 - returns the sine of an angle that lies between 0 and + * 30 degrees. This will return exactly 0 when x is 0, + * and exactly 0.5 when x is 30 degrees. + */ + static double + sind_0_to_30(double x) + { + return ( sin(x * (M_PI / 180.0)) / sin(30.0 * (M_PI / 180.0)) ) / 2.0; + } + + + /* + * cosd_0_to_60 - returns the cosine of an angle that lies between 0 + * and 60 degrees. This will return exactly 1 when x + * is 0 and exactly 0.5 when x is 60 degrees. + */ + static double + cosd_0_to_60(double x) + { + return ( 2.0 - (1.0 - cos(x * (M_PI / 180.0))) / + (1.0 - cos(60.0 * (M_PI / 180.0))) ) / 2.0; + } + + + /* + * sind_q1 - returns the sine of an angle in the first quadrant + * (0 to 90 degrees). + */ + static double + sind_q1(double x) + { + /* + * Stitch together the sine and cosine functions for the ranges [0, 30] + * and [30, 90]. These guarantee to return exact answers at their + * endpoints, so the overall result is a continuous monotonic function + * that gives exact results when x = 0, 30 and 90 degrees. + */ + return x <= 30.0 ? sind_0_to_30(x) : 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. + */ + return x <= 60.0 ? cosd_0_to_60(x) : 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; + + /* Reduce the range of the input to [0,90] degrees */ + arg1 = fmod(arg1, 360.0); + if (isnan(arg1)) + PG_RETURN_FLOAT8(arg1); + + 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; + + /* Reduce the range of the input to [0,90] degrees */ + arg1 = fmod(arg1, 360.0); + if (isnan(arg1)) + PG_RETURN_FLOAT8(arg1); + + 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; + + /* Reduce the range of the input to [0,90] degrees */ + arg1 = fmod(arg1, 360.0); + if (isnan(arg1)) + PG_RETURN_FLOAT8(arg1); + + 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; + + /* Reduce the range of the input to [0,90] degrees */ + arg1 = fmod(arg1, 360.0); + if (isnan(arg1)) + PG_RETURN_FLOAT8(arg1); + + 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 diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h new file mode 100644 index d8640db..f4d468e *** a/src/include/catalog/pg_proc.h --- b/src/include/catalog/pg_proc.h *************** DATA(insert OID = 1606 ( tan PGNSP P *** 1894,1899 **** --- 1894,1917 ---- DESCR("tangent"); DATA(insert OID = 1607 ( cot PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 701 "701" _null_ _null_ _null_ _null_ _null_ dcot _null_ _null_ _null_ )); DESCR("cotangent"); + + DATA(insert OID = 3317 ( 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 = 3318 ( 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 = 3319 ( 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 = 3320 ( 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 = 3321 ( 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 = 3322 ( 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 = 3323 ( 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 = 3324 ( cotd PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 701 "701" _null_ _null_ _null_ _null_ _null_ dcotd _null_ _null_ _null_ )); + DESCR("cotangent, degrees"); + DATA(insert OID = 1608 ( degrees PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 701 "701" _null_ _null_ _null_ _null_ _null_ degrees _null_ _null_ _null_ )); DESCR("radians to degrees"); DATA(insert OID = 1609 ( radians PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 701 "701" _null_ _null_ _null_ _null_ _null_ radians _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h new file mode 100644 index e610bf3..cbca7f3 *** a/src/include/utils/builtins.h --- b/src/include/utils/builtins.h *************** extern Datum dcos(PG_FUNCTION_ARGS); *** 407,412 **** --- 407,420 ---- extern Datum dcot(PG_FUNCTION_ARGS); extern Datum dsin(PG_FUNCTION_ARGS); extern Datum dtan(PG_FUNCTION_ARGS); + extern Datum dacosd(PG_FUNCTION_ARGS); + extern Datum dasind(PG_FUNCTION_ARGS); + extern Datum datand(PG_FUNCTION_ARGS); + extern Datum datan2d(PG_FUNCTION_ARGS); + extern Datum dcosd(PG_FUNCTION_ARGS); + extern Datum dcotd(PG_FUNCTION_ARGS); + extern Datum dsind(PG_FUNCTION_ARGS); + extern Datum dtand(PG_FUNCTION_ARGS); extern Datum degrees(PG_FUNCTION_ARGS); extern Datum dpi(PG_FUNCTION_ARGS); extern Datum radians(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/float8-exp-three-digits-win32.out b/src/test/regress/expected/float8-exp-three-digits-win32.out new file mode 100644 index 2dd648d..6891ee0 *** a/src/test/regress/expected/float8-exp-three-digits-win32.out --- b/src/test/regress/expected/float8-exp-three-digits-win32.out *************** SELECT '' AS five, * FROM FLOAT8_TBL; *** 444,446 **** --- 444,523 ---- | -1.2345678901234e-200 (5 rows) + -- test exact cases for trigonometric functions in degrees + SELECT x, + CASE WHEN sind(x) IN (-1,-0.5,0,0.5,1) THEN sind(x) END AS sind, + CASE WHEN cosd(x) IN (-1,-0.5,0,0.5,1) THEN cosd(x) END AS cosd, + CASE WHEN tand(x) IN ('-Infinity'::float8,-1,0, + 1,'Infinity'::float8) THEN tand(x) END AS tand, + CASE WHEN cotd(x) IN ('-Infinity'::float8,-1,0, + 1,'Infinity'::float8) THEN cotd(x) END AS cotd + FROM generate_series(0, 360, 15) AS t(x); + x | sind | cosd | tand | cotd + -----+------+------+-----------+----------- + 0 | 0 | 1 | 0 | Infinity + 15 | | | | + 30 | 0.5 | | | + 45 | | | 1 | 1 + 60 | | 0.5 | | + 75 | | | | + 90 | 1 | 0 | Infinity | 0 + 105 | | | | + 120 | | -0.5 | | + 135 | | | -1 | -1 + 150 | 0.5 | | | + 165 | | | | + 180 | 0 | -1 | -0 | -Infinity + 195 | | | | + 210 | -0.5 | | | + 225 | | | 1 | 1 + 240 | | -0.5 | | + 255 | | | | + 270 | -1 | 0 | -Infinity | -0 + 285 | | | | + 300 | | 0.5 | | + 315 | | | -1 | -1 + 330 | -0.5 | | | + 345 | | | | + 360 | 0 | 1 | 0 | Infinity + (25 rows) + + SELECT x, + CASE WHEN asind(x) IN (-90,-30,0,30,90) THEN asind(x) END AS asind, + CASE WHEN acosd(x) IN (0,60,90,120,180) THEN acosd(x) END AS acosd, + CASE WHEN atand(x) IN (-45,0,45) THEN atand(x) END AS atand + FROM (VALUES (-1), (-0.5), (0), (0.5), (1)) AS t(x); + x | asind | acosd | atand + ------+-------+-------+------- + -1 | -90 | 180 | -45 + -0.5 | -30 | 120 | + 0 | 0 | 90 | 0 + 0.5 | 30 | 60 | + 1 | 90 | 0 | 45 + (5 rows) + + SELECT atand('-Infinity'::float8) = -90; + ?column? + ---------- + t + (1 row) + + SELECT atand('Infinity'::float8) = 90; + ?column? + ---------- + t + (1 row) + + SELECT x, y, + CASE WHEN atan2d(y, x) IN (-90,0,90,180) THEN atan2d(y, x) END AS atan2d + FROM (SELECT 10*cosd(a), 10*sind(a) + FROM generate_series(0, 360, 90) AS t(a)) AS t(x,y); + x | y | atan2d + -----+-----+-------- + 10 | 0 | 0 + 0 | 10 | 90 + -10 | 0 | 180 + 0 | -10 | -90 + 10 | 0 | 0 + (5 rows) + diff --git a/src/test/regress/expected/float8-small-is-zero.out b/src/test/regress/expected/float8-small-is-zero.out new file mode 100644 index 5da7433..e158e70 *** a/src/test/regress/expected/float8-small-is-zero.out --- b/src/test/regress/expected/float8-small-is-zero.out *************** SELECT '' AS five, * FROM FLOAT8_TBL; *** 442,444 **** --- 442,521 ---- | -1.2345678901234e-200 (5 rows) + -- test exact cases for trigonometric functions in degrees + SELECT x, + CASE WHEN sind(x) IN (-1,-0.5,0,0.5,1) THEN sind(x) END AS sind, + CASE WHEN cosd(x) IN (-1,-0.5,0,0.5,1) THEN cosd(x) END AS cosd, + CASE WHEN tand(x) IN ('-Infinity'::float8,-1,0, + 1,'Infinity'::float8) THEN tand(x) END AS tand, + CASE WHEN cotd(x) IN ('-Infinity'::float8,-1,0, + 1,'Infinity'::float8) THEN cotd(x) END AS cotd + FROM generate_series(0, 360, 15) AS t(x); + x | sind | cosd | tand | cotd + -----+------+------+-----------+----------- + 0 | 0 | 1 | 0 | Infinity + 15 | | | | + 30 | 0.5 | | | + 45 | | | 1 | 1 + 60 | | 0.5 | | + 75 | | | | + 90 | 1 | 0 | Infinity | 0 + 105 | | | | + 120 | | -0.5 | | + 135 | | | -1 | -1 + 150 | 0.5 | | | + 165 | | | | + 180 | 0 | -1 | -0 | -Infinity + 195 | | | | + 210 | -0.5 | | | + 225 | | | 1 | 1 + 240 | | -0.5 | | + 255 | | | | + 270 | -1 | 0 | -Infinity | -0 + 285 | | | | + 300 | | 0.5 | | + 315 | | | -1 | -1 + 330 | -0.5 | | | + 345 | | | | + 360 | 0 | 1 | 0 | Infinity + (25 rows) + + SELECT x, + CASE WHEN asind(x) IN (-90,-30,0,30,90) THEN asind(x) END AS asind, + CASE WHEN acosd(x) IN (0,60,90,120,180) THEN acosd(x) END AS acosd, + CASE WHEN atand(x) IN (-45,0,45) THEN atand(x) END AS atand + FROM (VALUES (-1), (-0.5), (0), (0.5), (1)) AS t(x); + x | asind | acosd | atand + ------+-------+-------+------- + -1 | -90 | 180 | -45 + -0.5 | -30 | 120 | + 0 | 0 | 90 | 0 + 0.5 | 30 | 60 | + 1 | 90 | 0 | 45 + (5 rows) + + SELECT atand('-Infinity'::float8) = -90; + ?column? + ---------- + t + (1 row) + + SELECT atand('Infinity'::float8) = 90; + ?column? + ---------- + t + (1 row) + + SELECT x, y, + CASE WHEN atan2d(y, x) IN (-90,0,90,180) THEN atan2d(y, x) END AS atan2d + FROM (SELECT 10*cosd(a), 10*sind(a) + FROM generate_series(0, 360, 90) AS t(a)) AS t(x,y); + x | y | atan2d + -----+-----+-------- + 10 | 0 | 0 + 0 | 10 | 90 + -10 | 0 | 180 + 0 | -10 | -90 + 10 | 0 | 0 + (5 rows) + diff --git a/src/test/regress/expected/float8-small-is-zero_1.out b/src/test/regress/expected/float8-small-is-zero_1.out new file mode 100644 index 530842e..42e50a0 *** a/src/test/regress/expected/float8-small-is-zero_1.out --- b/src/test/regress/expected/float8-small-is-zero_1.out *************** SELECT '' AS five, * FROM FLOAT8_TBL; *** 442,444 **** --- 442,521 ---- | -1.2345678901234e-200 (5 rows) + -- test exact cases for trigonometric functions in degrees + SELECT x, + CASE WHEN sind(x) IN (-1,-0.5,0,0.5,1) THEN sind(x) END AS sind, + CASE WHEN cosd(x) IN (-1,-0.5,0,0.5,1) THEN cosd(x) END AS cosd, + CASE WHEN tand(x) IN ('-Infinity'::float8,-1,0, + 1,'Infinity'::float8) THEN tand(x) END AS tand, + CASE WHEN cotd(x) IN ('-Infinity'::float8,-1,0, + 1,'Infinity'::float8) THEN cotd(x) END AS cotd + FROM generate_series(0, 360, 15) AS t(x); + x | sind | cosd | tand | cotd + -----+------+------+-----------+----------- + 0 | 0 | 1 | 0 | Infinity + 15 | | | | + 30 | 0.5 | | | + 45 | | | 1 | 1 + 60 | | 0.5 | | + 75 | | | | + 90 | 1 | 0 | Infinity | 0 + 105 | | | | + 120 | | -0.5 | | + 135 | | | -1 | -1 + 150 | 0.5 | | | + 165 | | | | + 180 | 0 | -1 | -0 | -Infinity + 195 | | | | + 210 | -0.5 | | | + 225 | | | 1 | 1 + 240 | | -0.5 | | + 255 | | | | + 270 | -1 | 0 | -Infinity | -0 + 285 | | | | + 300 | | 0.5 | | + 315 | | | -1 | -1 + 330 | -0.5 | | | + 345 | | | | + 360 | 0 | 1 | 0 | Infinity + (25 rows) + + SELECT x, + CASE WHEN asind(x) IN (-90,-30,0,30,90) THEN asind(x) END AS asind, + CASE WHEN acosd(x) IN (0,60,90,120,180) THEN acosd(x) END AS acosd, + CASE WHEN atand(x) IN (-45,0,45) THEN atand(x) END AS atand + FROM (VALUES (-1), (-0.5), (0), (0.5), (1)) AS t(x); + x | asind | acosd | atand + ------+-------+-------+------- + -1 | -90 | 180 | -45 + -0.5 | -30 | 120 | + 0 | 0 | 90 | 0 + 0.5 | 30 | 60 | + 1 | 90 | 0 | 45 + (5 rows) + + SELECT atand('-Infinity'::float8) = -90; + ?column? + ---------- + t + (1 row) + + SELECT atand('Infinity'::float8) = 90; + ?column? + ---------- + t + (1 row) + + SELECT x, y, + CASE WHEN atan2d(y, x) IN (-90,0,90,180) THEN atan2d(y, x) END AS atan2d + FROM (SELECT 10*cosd(a), 10*sind(a) + FROM generate_series(0, 360, 90) AS t(a)) AS t(x,y); + x | y | atan2d + -----+-----+-------- + 10 | 0 | 0 + 0 | 10 | 90 + -10 | 0 | 180 + 0 | -10 | -90 + 10 | 0 | 0 + (5 rows) + diff --git a/src/test/regress/expected/float8.out b/src/test/regress/expected/float8.out new file mode 100644 index 6221538..b77b34f *** a/src/test/regress/expected/float8.out --- b/src/test/regress/expected/float8.out *************** SELECT '' AS five, * FROM FLOAT8_TBL; *** 444,446 **** --- 444,523 ---- | -1.2345678901234e-200 (5 rows) + -- test exact cases for trigonometric functions in degrees + SELECT x, + CASE WHEN sind(x) IN (-1,-0.5,0,0.5,1) THEN sind(x) END AS sind, + CASE WHEN cosd(x) IN (-1,-0.5,0,0.5,1) THEN cosd(x) END AS cosd, + CASE WHEN tand(x) IN ('-Infinity'::float8,-1,0, + 1,'Infinity'::float8) THEN tand(x) END AS tand, + CASE WHEN cotd(x) IN ('-Infinity'::float8,-1,0, + 1,'Infinity'::float8) THEN cotd(x) END AS cotd + FROM generate_series(0, 360, 15) AS t(x); + x | sind | cosd | tand | cotd + -----+------+------+-----------+----------- + 0 | 0 | 1 | 0 | Infinity + 15 | | | | + 30 | 0.5 | | | + 45 | | | 1 | 1 + 60 | | 0.5 | | + 75 | | | | + 90 | 1 | 0 | Infinity | 0 + 105 | | | | + 120 | | -0.5 | | + 135 | | | -1 | -1 + 150 | 0.5 | | | + 165 | | | | + 180 | 0 | -1 | -0 | -Infinity + 195 | | | | + 210 | -0.5 | | | + 225 | | | 1 | 1 + 240 | | -0.5 | | + 255 | | | | + 270 | -1 | 0 | -Infinity | -0 + 285 | | | | + 300 | | 0.5 | | + 315 | | | -1 | -1 + 330 | -0.5 | | | + 345 | | | | + 360 | 0 | 1 | 0 | Infinity + (25 rows) + + SELECT x, + CASE WHEN asind(x) IN (-90,-30,0,30,90) THEN asind(x) END AS asind, + CASE WHEN acosd(x) IN (0,60,90,120,180) THEN acosd(x) END AS acosd, + CASE WHEN atand(x) IN (-45,0,45) THEN atand(x) END AS atand + FROM (VALUES (-1), (-0.5), (0), (0.5), (1)) AS t(x); + x | asind | acosd | atand + ------+-------+-------+------- + -1 | -90 | 180 | -45 + -0.5 | -30 | 120 | + 0 | 0 | 90 | 0 + 0.5 | 30 | 60 | + 1 | 90 | 0 | 45 + (5 rows) + + SELECT atand('-Infinity'::float8) = -90; + ?column? + ---------- + t + (1 row) + + SELECT atand('Infinity'::float8) = 90; + ?column? + ---------- + t + (1 row) + + SELECT x, y, + CASE WHEN atan2d(y, x) IN (-90,0,90,180) THEN atan2d(y, x) END AS atan2d + FROM (SELECT 10*cosd(a), 10*sind(a) + FROM generate_series(0, 360, 90) AS t(a)) AS t(x,y); + x | y | atan2d + -----+-----+-------- + 10 | 0 | 0 + 0 | 10 | 90 + -10 | 0 | 180 + 0 | -10 | -90 + 10 | 0 | 0 + (5 rows) + diff --git a/src/test/regress/sql/float8.sql b/src/test/regress/sql/float8.sql new file mode 100644 index 92a574a..45a484b *** a/src/test/regress/sql/float8.sql --- b/src/test/regress/sql/float8.sql *************** INSERT INTO FLOAT8_TBL(f1) VALUES ('-1.2 *** 167,169 **** --- 167,193 ---- INSERT INTO FLOAT8_TBL(f1) VALUES ('-1.2345678901234e-200'); SELECT '' AS five, * FROM FLOAT8_TBL; + + -- test exact cases for trigonometric functions in degrees + SELECT x, + CASE WHEN sind(x) IN (-1,-0.5,0,0.5,1) THEN sind(x) END AS sind, + CASE WHEN cosd(x) IN (-1,-0.5,0,0.5,1) THEN cosd(x) END AS cosd, + CASE WHEN tand(x) IN ('-Infinity'::float8,-1,0, + 1,'Infinity'::float8) THEN tand(x) END AS tand, + CASE WHEN cotd(x) IN ('-Infinity'::float8,-1,0, + 1,'Infinity'::float8) THEN cotd(x) END AS cotd + FROM generate_series(0, 360, 15) AS t(x); + + SELECT x, + CASE WHEN asind(x) IN (-90,-30,0,30,90) THEN asind(x) END AS asind, + CASE WHEN acosd(x) IN (0,60,90,120,180) THEN acosd(x) END AS acosd, + CASE WHEN atand(x) IN (-45,0,45) THEN atand(x) END AS atand + FROM (VALUES (-1), (-0.5), (0), (0.5), (1)) AS t(x); + + SELECT atand('-Infinity'::float8) = -90; + SELECT atand('Infinity'::float8) = 90; + + SELECT x, y, + CASE WHEN atan2d(y, x) IN (-90,0,90,180) THEN atan2d(y, x) END AS atan2d + FROM (SELECT 10*cosd(a), 10*sind(a) + FROM generate_series(0, 360, 90) AS t(a)) AS t(x,y);
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers