@@ -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 */
17681817if (x <=0.5 )
1769- return (asin (x ) /asin ( 0.5 ) )* 30.0 ;
1818+ return (asin (x ) /asin_0_5 )* 30.0 ;
17701819else
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 */
17931842if (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 ;
17951844else
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)
18101859if (isnan (arg1 ))
18111860PG_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)
18431894if (isnan (arg1 ))
18441895PG_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)
18761929if (isnan (arg1 ))
18771930PG_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
18871942CHECKFLOATVAL (result , false, true);
18881943PG_RETURN_FLOAT8 (result );
@@ -1903,11 +1958,13 @@ datan2d(PG_FUNCTION_ARGS)
19031958if (isnan (arg1 )|| isnan (arg2 ))
19041959PG_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
19121969CHECKFLOATVAL (result , false, true);
19131970PG_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{
19882044float8 arg1 = PG_GETARG_FLOAT8 (0 );
1989- int sign = 1 ;
19902045float8 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 ),
20022058errmsg ("input is out of range" )));
20032059
2060+ INIT_DEGREE_CONSTANTS ();
2061+
20042062/* Reduce the range of the input to [0,90] degrees */
20052063arg1 = fmod (arg1 ,360.0 );
20062064
20072065if (arg1 < 0.0 )
2066+ {
20082067/* cosd(-x) = cosd(x) */
20092068arg1 = - arg1 ;
2069+ }
20102070
20112071if (arg1 > 180.0 )
2072+ {
20122073/* cosd(360-x) = cosd(x) */
20132074arg1 = 360.0 - arg1 ;
2075+ }
20142076
20152077if (arg1 > 90.0 )
20162078{
@@ -2035,7 +2097,6 @@ dcotd(PG_FUNCTION_ARGS)
20352097float8 arg1 = PG_GETARG_FLOAT8 (0 );
20362098float8 result ;
20372099int 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 ),
20502111errmsg ("input is out of range" )));
20512112
2113+ INIT_DEGREE_CONSTANTS ();
2114+
20522115/* Reduce the range of the input to [0,90] degrees */
20532116arg1 = fmod (arg1 ,360.0 );
20542117
@@ -2073,22 +2136,10 @@ dcotd(PG_FUNCTION_ARGS)
20732136sign = - 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{
21092160float8 arg1 = PG_GETARG_FLOAT8 (0 );
2110- int sign = 1 ;
21112161float8 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 ),
21232174errmsg ("input is out of range" )));
21242175
2176+ INIT_DEGREE_CONSTANTS ();
2177+
21252178/* Reduce the range of the input to [0,90] degrees */
21262179arg1 = fmod (arg1 ,360.0 );
21272180
@@ -2140,8 +2193,10 @@ dsind(PG_FUNCTION_ARGS)
21402193}
21412194
21422195if (arg1 > 90.0 )
2196+ {
21432197/* sind(180-x) = sind(x) */
21442198arg1 = 180.0 - arg1 ;
2199+ }
21452200
21462201result = sign * sind_q1 (arg1 );
21472202
@@ -2159,7 +2214,6 @@ dtand(PG_FUNCTION_ARGS)
21592214float8 arg1 = PG_GETARG_FLOAT8 (0 );
21602215float8 result ;
21612216int 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 ),
21742228errmsg ("input is out of range" )));
21752229
2230+ INIT_DEGREE_CONSTANTS ();
2231+
21762232/* Reduce the range of the input to [0,90] degrees */
21772233arg1 = fmod (arg1 ,360.0 );
21782234
@@ -2197,22 +2253,10 @@ dtand(PG_FUNCTION_ARGS)
21972253sign = - 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 */