diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml new file mode 100644 index 0af01d9..d9a218c *** a/doc/src/sgml/func.sgml --- b/doc/src/sgml/func.sgml *************** *** 1006,1014 **** Finally, shows the available trigonometric functions. All trigonometric functions take arguments and return values of type double ! precision. Trigonometric functions arguments are expressed ! in radians. Inverse functions return values are expressed in ! radians. See unit transformation functions radians() and degrees() above. --- 1006,1014 ---- Finally, shows the available trigonometric functions. All trigonometric functions take arguments and return values of type double ! precision. Each of the trigonometric functions comes in ! two varieties, one which works in radians and one which works in ! degrees. See unit transformation functions radians() and degrees() above. *************** *** 1016,1025 **** Trigonometric Functions ! ! Function Description --- 1016,1026 ----
Trigonometric Functions ! ! Function (radians) ! Function (degrees) Description *************** *** 1031,1036 **** --- 1032,1042 ---- acosacos(x) + + + acosd + acosd(x) + inverse cosine *************** *** 1041,1046 **** --- 1047,1058 ---- asin(x) + + + asind + + asind(x) + inverse sine *************** *** 1051,1056 **** --- 1063,1074 ---- atan(x) + + + atand + + atand(x) + inverse tangent *************** *** 1062,1067 **** --- 1080,1092 ---- atan2(y, x) + + + atan2d + + atan2d(y, + x) + inverse tangent of y/x *************** *** 1073,1078 **** --- 1098,1109 ---- cos(x) + + + cosd + + cosd(x) + cosine *************** *** 1083,1088 **** --- 1114,1125 ---- cot(x) + + + cotd + + cotd(x) + cotangent *************** *** 1093,1098 **** --- 1130,1141 ---- sin(x) + + + sind + + sind(x) + sine *************** *** 1103,1108 **** --- 1146,1157 ---- tan(x) + + + tand + + tand(x) + tangent diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c new file mode 100644 index 8f34209..9d48406 *** a/src/backend/utils/adt/float.c --- b/src/backend/utils/adt/float.c *************** dtan(PG_FUNCTION_ARGS) *** 1690,1695 **** --- 1690,2130 ---- /* + * 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 arctan2 of arg1 (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 ( 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. + */ + 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 /* result is always finite */ , 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 /* result is always finite */ , 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 */ Datum diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h new file mode 100644 index f58672e..3c67b91 *** 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 = 3318 ( 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 = 3319 ( 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 = 3320 ( 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 = 3321 ( 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 = 3322 ( 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 = 3323 ( 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 = 3324 ( 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 = 3325 ( 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 b35d206..75f1d20 *** 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);