Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit65abaab

Browse files
committed
Further adjust degree-based trig functions for more portability.
The last round didn't do it. Per Noah Misch, the problem on at leastsome machines is that the compiler pre-evaluates trig functions havingconstant arguments using code slightly different from what will be usedat runtime. Therefore, we must prevent the compiler from seeing constantarguments to any of the libm trig functions used in this code.The method used here might still fail if init_degree_constants() getsinlined into the call sites. That probably won't happen given the largenumber of call sites; but if it does, we could probably fix it by makinginit_degree_constants() non-static. I'll avoid that till provennecessary, though.
1 parent73193d8 commit65abaab

File tree

1 file changed

+89
-45
lines changed

1 file changed

+89
-45
lines changed

‎src/backend/utils/adt/float.c

Lines changed: 89 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -64,15 +64,24 @@ do {\
6464
} while(0)
6565

6666

67-
/* ========== USER I/O ROUTINES ========== */
68-
69-
7067
/* Configurable GUC parameter */
7168
intextra_float_digits=0;/* Added to DBL_DIG or FLT_DIG */
7269

73-
70+
/* Cached constants for degree-based trig functions */
71+
staticbooldegree_consts_set= false;
72+
staticfloat8sin_30=0;
73+
staticfloat8one_minus_cos_60=0;
74+
staticfloat8asin_0_5=0;
75+
staticfloat8acos_0_5=0;
76+
staticfloat8atan_1_0=0;
77+
staticfloat8tan_45=0;
78+
staticfloat8cot_45=0;
79+
80+
/* Local function prototypes */
7481
staticintfloat4_cmp_internal(float4a,float4b);
7582
staticintfloat8_cmp_internal(float8a,float8b);
83+
staticdoublesind_q1(doublex);
84+
staticdoublecosd_q1(doublex);
7685

7786
#ifndefHAVE_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+
staticvoid
1779+
init_degree_constants(float8thirty,float8forty_five,float8sixty,
1780+
float8one_half,float8one)
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+
#defineINIT_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-
return90.0- (acos(x) /acos(0.5))*60.0;
1820+
return90.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-
return90.0- (asin(x) /asin(0.5))*30.0;
1843+
return90.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)
19221979
staticdouble
19231980
sind_0_to_30(doublex)
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)
19341991
staticdouble
19351992
cosd_0_to_60(doublex)
19361993
{
1937-
return1.0- ((1.0-cos(x*RADIANS_PER_DEGREE)) /
1938-
(1.0-cos(60.0*RADIANS_PER_DEGREE))) /2.0;
1994+
return1.0- ((1.0-cos(x*RADIANS_PER_DEGREE)) /one_minus_cos_60) /2.0;
19391995
}
19401996

19411997

@@ -1986,8 +2042,8 @@ Datum
19862042
dcosd(PG_FUNCTION_ARGS)
19872043
{
19882044
float8arg1=PG_GETARG_FLOAT8(0);
1989-
intsign=1;
19902045
float8result;
2046+
intsign=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
float8arg1=PG_GETARG_FLOAT8(0);
20362098
float8result;
20372099
intsign=1;
2038-
staticfloat8cot45=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
21072158
dsind(PG_FUNCTION_ARGS)
21082159
{
21092160
float8arg1=PG_GETARG_FLOAT8(0);
2110-
intsign=1;
21112161
float8result;
2162+
intsign=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
float8arg1=PG_GETARG_FLOAT8(0);
21602215
float8result;
21612216
intsign=1;
2162-
staticfloat8tan45=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

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp