@@ -64,15 +64,24 @@ do { \
6464} while(0)
6565
6666
67- /* ========== USER I/O ROUTINES ========== */
68-
69-
7067/* Configurable GUC parameter */
7168int extra_float_digits = 0 ; /* Added to DBL_DIG or FLT_DIG */
7269
73-
70+ /* Cached constants for degree-based trig functions */
71+ static bool degree_consts_set = false;
72+ static float8 sin_30 = 0 ;
73+ static float8 one_minus_cos_60 = 0 ;
74+ static float8 asin_0_5 = 0 ;
75+ static float8 acos_0_5 = 0 ;
76+ static float8 atan_1_0 = 0 ;
77+ static float8 tan_45 = 0 ;
78+ static float8 cot_45 = 0 ;
79+
80+ /* Local function prototypes */
7481static int float4_cmp_internal (float4 a , float4 b );
7582static int float8_cmp_internal (float8 a , float8 b );
83+ static double sind_q1 (double x );
84+ static double cosd_q1 (double x );
7685
7786#ifndef HAVE_CBRT
7887/*
@@ -189,6 +198,9 @@ is_infinite(double val)
189198}
190199
191200
201+ /* ========== USER I/O ROUTINES ========== */
202+
203+
192204/*
193205 * float4in - converts "num" to float4
194206 */
@@ -1747,6 +1759,43 @@ dtan(PG_FUNCTION_ARGS)
17471759}
17481760
17491761
1762+ /* ========== DEGREE-BASED TRIGONOMETRIC FUNCTIONS ========== */
1763+
1764+
1765+ /*
1766+ * Initialize the cached constants declared at the head of this file
1767+ * (sin_30 etc). The fact that we need those at all, let alone need this
1768+ * Rube-Goldberg-worthy method of initializing them, is because there are
1769+ * compilers out there that will precompute expressions such as sin(constant)
1770+ * using a sin() function different from what will be used at runtime. If we
1771+ * want exact results, we must ensure that none of the scaling constants used
1772+ * in the degree-based trig functions are computed that way.
1773+ *
1774+ * Other hazards we are trying to forestall with this kluge include the
1775+ * possibility that compilers will rearrange the expressions, or compute
1776+ * some intermediate results in registers wider than a standard double.
1777+ */
1778+ static void
1779+ init_degree_constants (float8 thirty , float8 forty_five , float8 sixty ,
1780+ float8 one_half , float8 one )
1781+ {
1782+ sin_30 = sin (thirty * RADIANS_PER_DEGREE );
1783+ one_minus_cos_60 = 1.0 - cos (sixty * RADIANS_PER_DEGREE );
1784+ asin_0_5 = asin (one_half );
1785+ acos_0_5 = acos (one_half );
1786+ atan_1_0 = atan (one );
1787+ tan_45 = sind_q1 (forty_five ) / cosd_q1 (forty_five );
1788+ cot_45 = cosd_q1 (forty_five ) / sind_q1 (forty_five );
1789+ degree_consts_set = true;
1790+ }
1791+
1792+ #define INIT_DEGREE_CONSTANTS () \
1793+ do { \
1794+ if (!degree_consts_set) \
1795+ init_degree_constants(30.0, 45.0, 60.0, 0.5, 1.0); \
1796+ } while(0)
1797+
1798+
17501799/*
17511800 * asind_q1 - returns the inverse sine of x in degrees, for x in
17521801 * the range [0, 1]. The result is an angle in the
@@ -1766,9 +1815,9 @@ asind_q1(double x)
17661815 * over the full range.
17671816 */
17681817 if (x <= 0.5 )
1769- return (asin (x ) / asin ( 0.5 ) ) * 30.0 ;
1818+ return (asin (x ) / asin_0_5 ) * 30.0 ;
17701819 else
1771- return 90.0 - (acos (x ) / acos ( 0.5 ) ) * 60.0 ;
1820+ return 90.0 - (acos (x ) / acos_0_5 ) * 60.0 ;
17721821}
17731822
17741823
@@ -1791,9 +1840,9 @@ acosd_q1(double x)
17911840 * over the full range.
17921841 */
17931842 if (x <= 0.5 )
1794- return 90.0 - (asin (x ) / asin ( 0.5 ) ) * 30.0 ;
1843+ return 90.0 - (asin (x ) / asin_0_5 ) * 30.0 ;
17951844 else
1796- return (acos (x ) / acos ( 0.5 ) ) * 60.0 ;
1845+ return (acos (x ) / acos_0_5 ) * 60.0 ;
17971846}
17981847
17991848
@@ -1810,6 +1859,8 @@ dacosd(PG_FUNCTION_ARGS)
18101859 if (isnan (arg1 ))
18111860 PG_RETURN_FLOAT8 (get_float8_nan ());
18121861
1862+ INIT_DEGREE_CONSTANTS ();
1863+
18131864 /*
18141865 * The principal branch of the inverse cosine function maps values in the
18151866 * range [-1, 1] to values in the range [0, 180], so we should reject any
@@ -1843,6 +1894,8 @@ dasind(PG_FUNCTION_ARGS)
18431894 if (isnan (arg1 ))
18441895 PG_RETURN_FLOAT8 (get_float8_nan ());
18451896
1897+ INIT_DEGREE_CONSTANTS ();
1898+
18461899 /*
18471900 * The principal branch of the inverse sine function maps values in the
18481901 * range [-1, 1] to values in the range [-90, 90], so we should reject any
@@ -1876,13 +1929,15 @@ datand(PG_FUNCTION_ARGS)
18761929 if (isnan (arg1 ))
18771930 PG_RETURN_FLOAT8 (get_float8_nan ());
18781931
1932+ INIT_DEGREE_CONSTANTS ();
1933+
18791934 /*
18801935 * The principal branch of the inverse tangent function maps all inputs to
18811936 * values in the range [-90, 90], so the result should always be finite,
18821937 * even if the input is infinite. Additionally, we take care to ensure
18831938 * than when arg1 is 1, the result is exactly 45.
18841939 */
1885- result = (atan (arg1 ) / atan ( 1.0 ) ) * 45.0 ;
1940+ result = (atan (arg1 ) / atan_1_0 ) * 45.0 ;
18861941
18871942 CHECKFLOATVAL (result , false, true);
18881943 PG_RETURN_FLOAT8 (result );
@@ -1903,11 +1958,13 @@ datan2d(PG_FUNCTION_ARGS)
19031958 if (isnan (arg1 ) || isnan (arg2 ))
19041959 PG_RETURN_FLOAT8 (get_float8_nan ());
19051960
1961+ INIT_DEGREE_CONSTANTS ();
1962+
19061963 /*
19071964 * atan2d maps all inputs to values in the range [-180, 180], so the
19081965 * result should always be finite, even if the inputs are infinite.
19091966 */
1910- result = (atan2 (arg1 , arg2 ) / atan ( 1.0 ) ) * 45.0 ;
1967+ result = (atan2 (arg1 , arg2 ) / atan_1_0 ) * 45.0 ;
19111968
19121969 CHECKFLOATVAL (result , false, true);
19131970 PG_RETURN_FLOAT8 (result );
@@ -1922,7 +1979,7 @@ datan2d(PG_FUNCTION_ARGS)
19221979static double
19231980sind_0_to_30 (double x )
19241981{
1925- return (sin (x * RADIANS_PER_DEGREE ) / sin ( 30.0 * RADIANS_PER_DEGREE ) ) / 2.0 ;
1982+ return (sin (x * RADIANS_PER_DEGREE ) / sin_30 ) / 2.0 ;
19261983}
19271984
19281985
@@ -1934,8 +1991,7 @@ sind_0_to_30(double x)
19341991static double
19351992cosd_0_to_60 (double x )
19361993{
1937- return 1.0 - ((1.0 - cos (x * RADIANS_PER_DEGREE )) /
1938- (1.0 - cos (60.0 * RADIANS_PER_DEGREE ))) / 2.0 ;
1994+ return 1.0 - ((1.0 - cos (x * RADIANS_PER_DEGREE )) / one_minus_cos_60 ) / 2.0 ;
19391995}
19401996
19411997
@@ -1986,8 +2042,8 @@ Datum
19862042dcosd (PG_FUNCTION_ARGS )
19872043{
19882044 float8 arg1 = PG_GETARG_FLOAT8 (0 );
1989- int sign = 1 ;
19902045 float8 result ;
2046+ int sign = 1 ;
19912047
19922048 /*
19932049 * Per the POSIX spec, return NaN if the input is NaN and throw an error
@@ -2001,16 +2057,22 @@ dcosd(PG_FUNCTION_ARGS)
20012057 (errcode (ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE ),
20022058 errmsg ("input is out of range" )));
20032059
2060+ INIT_DEGREE_CONSTANTS ();
2061+
20042062 /* Reduce the range of the input to [0,90] degrees */
20052063 arg1 = fmod (arg1 , 360.0 );
20062064
20072065 if (arg1 < 0.0 )
2066+ {
20082067 /* cosd(-x) = cosd(x) */
20092068 arg1 = - arg1 ;
2069+ }
20102070
20112071 if (arg1 > 180.0 )
2072+ {
20122073 /* cosd(360-x) = cosd(x) */
20132074 arg1 = 360.0 - arg1 ;
2075+ }
20142076
20152077 if (arg1 > 90.0 )
20162078 {
@@ -2035,7 +2097,6 @@ dcotd(PG_FUNCTION_ARGS)
20352097 float8 arg1 = PG_GETARG_FLOAT8 (0 );
20362098 float8 result ;
20372099 int sign = 1 ;
2038- static float8 cot45 = 0.0 ;
20392100
20402101 /*
20412102 * Per the POSIX spec, return NaN if the input is NaN and throw an error
@@ -2049,6 +2110,8 @@ dcotd(PG_FUNCTION_ARGS)
20492110 (errcode (ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE ),
20502111 errmsg ("input is out of range" )));
20512112
2113+ INIT_DEGREE_CONSTANTS ();
2114+
20522115 /* Reduce the range of the input to [0,90] degrees */
20532116 arg1 = fmod (arg1 , 360.0 );
20542117
@@ -2073,22 +2136,10 @@ dcotd(PG_FUNCTION_ARGS)
20732136 sign = - sign ;
20742137 }
20752138
2076- result = sign * cosd_q1 (arg1 ) / sind_q1 (arg1 );
2077-
2078- /*
2079- * We want cotd(45) to be exactly 1, but the above computation might've
2080- * produced something different, so scale to get the right result. To
2081- * avoid redoing cosd_q1(45) / sind_q1(45) many times, and to prevent the
2082- * compiler from maybe rearranging the calculation, cache that value in a
2083- * static variable.
2084- */
2085- if (cot45 == 0.0 )
2086- cot45 = cosd_q1 (45.0 ) / sind_q1 (45.0 );
2087-
2088- result /= cot45 ;
2139+ result = sign * ((cosd_q1 (arg1 ) / sind_q1 (arg1 )) / cot_45 );
20892140
20902141 /*
2091- * On some machines, we get cotd(270) = minus zero, but this isn't always
2142+ * On some machines we get cotd(270) = minus zero, but this isn't always
20922143 * true. For portability, and because the user constituency for this
20932144 * function probably doesn't want minus zero, force it to plain zero.
20942145 */
@@ -2107,8 +2158,8 @@ Datum
21072158dsind (PG_FUNCTION_ARGS )
21082159{
21092160 float8 arg1 = PG_GETARG_FLOAT8 (0 );
2110- int sign = 1 ;
21112161 float8 result ;
2162+ int sign = 1 ;
21122163
21132164 /*
21142165 * Per the POSIX spec, return NaN if the input is NaN and throw an error
@@ -2122,6 +2173,8 @@ dsind(PG_FUNCTION_ARGS)
21222173 (errcode (ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE ),
21232174 errmsg ("input is out of range" )));
21242175
2176+ INIT_DEGREE_CONSTANTS ();
2177+
21252178 /* Reduce the range of the input to [0,90] degrees */
21262179 arg1 = fmod (arg1 , 360.0 );
21272180
@@ -2140,8 +2193,10 @@ dsind(PG_FUNCTION_ARGS)
21402193 }
21412194
21422195 if (arg1 > 90.0 )
2196+ {
21432197 /* sind(180-x) = sind(x) */
21442198 arg1 = 180.0 - arg1 ;
2199+ }
21452200
21462201 result = sign * sind_q1 (arg1 );
21472202
@@ -2159,7 +2214,6 @@ dtand(PG_FUNCTION_ARGS)
21592214 float8 arg1 = PG_GETARG_FLOAT8 (0 );
21602215 float8 result ;
21612216 int sign = 1 ;
2162- static float8 tan45 = 0.0 ;
21632217
21642218 /*
21652219 * Per the POSIX spec, return NaN if the input is NaN and throw an error
@@ -2173,6 +2227,8 @@ dtand(PG_FUNCTION_ARGS)
21732227 (errcode (ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE ),
21742228 errmsg ("input is out of range" )));
21752229
2230+ INIT_DEGREE_CONSTANTS ();
2231+
21762232 /* Reduce the range of the input to [0,90] degrees */
21772233 arg1 = fmod (arg1 , 360.0 );
21782234
@@ -2197,22 +2253,10 @@ dtand(PG_FUNCTION_ARGS)
21972253 sign = - sign ;
21982254 }
21992255
2200- result = sign * sind_q1 (arg1 ) / cosd_q1 (arg1 );
2201-
2202- /*
2203- * We want tand(45) to be exactly 1, but the above computation might've
2204- * produced something different, so scale to get the right result. To
2205- * avoid redoing sind_q1(45) / cosd_q1(45) many times, and to prevent the
2206- * compiler from maybe rearranging the calculation, cache that value in a
2207- * static variable.
2208- */
2209- if (tan45 == 0.0 )
2210- tan45 = sind_q1 (45.0 ) / cosd_q1 (45.0 );
2211-
2212- result /= tan45 ;
2256+ result = sign * ((sind_q1 (arg1 ) / cosd_q1 (arg1 )) / tan_45 );
22132257
22142258 /*
2215- * On some machines, we get tand(180) = minus zero, but this isn't always
2259+ * On some machines we get tand(180) = minus zero, but this isn't always
22162260 * true. For portability, and because the user constituency for this
22172261 * function probably doesn't want minus zero, force it to plain zero.
22182262 */
0 commit comments