Skip to content

Commit 82311bc

Browse files
committed
Yet more portability hacking for degree-based trig functions.
The true explanation for Peter Eisentraut's report of inexact asind results seems to be that (a) he's compiling into x87 instruction set, which uses wider-than-double float registers, plus (b) the library function asin() on his platform returns a result that is wider than double and is not rounded to double width. To fix, we have to force the function's result to be rounded comparably to what happened to the scaling constant asin_0_5. Experimentation suggests that storing it into a volatile local variable is the least ugly way of making that happen. Although only asin() is known to exhibit an observable inexact result, we'd better do this in all the places where we're hoping to get an exact result by scaling.
1 parent 77cd477 commit 82311bc

File tree

1 file changed

+54
-10
lines changed

1 file changed

+54
-10
lines changed

src/backend/utils/adt/float.c

Lines changed: 54 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1830,6 +1830,19 @@ dtan(PG_FUNCTION_ARGS)
18301830
* Other hazards we are trying to forestall with this kluge include the
18311831
* possibility that compilers will rearrange the expressions, or compute
18321832
* some intermediate results in registers wider than a standard double.
1833+
*
1834+
* In the places where we use these constants, the typical pattern is like
1835+
* volatile float8 sin_x = sin(x * RADIANS_PER_DEGREE);
1836+
* return (sin_x / sin_30);
1837+
* where we hope to get a value of exactly 1.0 from the division when x = 30.
1838+
* The volatile temporary variable is needed on machines with wide float
1839+
* registers, to ensure that the result of sin(x) is rounded to double width
1840+
* the same as the value of sin_30 has been. Experimentation with gcc shows
1841+
* that marking the temp variable volatile is necessary to make the store and
1842+
* reload actually happen; hopefully the same trick works for other compilers.
1843+
* (gcc's documentation suggests using the -ffloat-store compiler switch to
1844+
* ensure this, but that is compiler-specific and it also pessimizes code in
1845+
* many places where we don't care about this.)
18331846
*/
18341847
static void
18351848
init_degree_constants(void)
@@ -1870,9 +1883,17 @@ asind_q1(double x)
18701883
* over the full range.
18711884
*/
18721885
if (x <= 0.5)
1873-
return (asin(x) / asin_0_5) * 30.0;
1886+
{
1887+
volatile float8 asin_x = asin(x);
1888+
1889+
return (asin_x / asin_0_5) * 30.0;
1890+
}
18741891
else
1875-
return 90.0 - (acos(x) / acos_0_5) * 60.0;
1892+
{
1893+
volatile float8 acos_x = acos(x);
1894+
1895+
return 90.0 - (acos_x / acos_0_5) * 60.0;
1896+
}
18761897
}
18771898

18781899

@@ -1895,9 +1916,17 @@ acosd_q1(double x)
18951916
* over the full range.
18961917
*/
18971918
if (x <= 0.5)
1898-
return 90.0 - (asin(x) / asin_0_5) * 30.0;
1919+
{
1920+
volatile float8 asin_x = asin(x);
1921+
1922+
return 90.0 - (asin_x / asin_0_5) * 30.0;
1923+
}
18991924
else
1900-
return (acos(x) / acos_0_5) * 60.0;
1925+
{
1926+
volatile float8 acos_x = acos(x);
1927+
1928+
return (acos_x / acos_0_5) * 60.0;
1929+
}
19011930
}
19021931

19031932

@@ -1979,6 +2008,7 @@ datand(PG_FUNCTION_ARGS)
19792008
{
19802009
float8 arg1 = PG_GETARG_FLOAT8(0);
19812010
float8 result;
2011+
volatile float8 atan_arg1;
19822012

19832013
/* Per the POSIX spec, return NaN if the input is NaN */
19842014
if (isnan(arg1))
@@ -1992,7 +2022,8 @@ datand(PG_FUNCTION_ARGS)
19922022
* even if the input is infinite. Additionally, we take care to ensure
19932023
* than when arg1 is 1, the result is exactly 45.
19942024
*/
1995-
result = (atan(arg1) / atan_1_0) * 45.0;
2025+
atan_arg1 = atan(arg1);
2026+
result = (atan_arg1 / atan_1_0) * 45.0;
19962027

19972028
CHECKFLOATVAL(result, false, true);
19982029
PG_RETURN_FLOAT8(result);
@@ -2008,6 +2039,7 @@ datan2d(PG_FUNCTION_ARGS)
20082039
float8 arg1 = PG_GETARG_FLOAT8(0);
20092040
float8 arg2 = PG_GETARG_FLOAT8(1);
20102041
float8 result;
2042+
volatile float8 atan2_arg1_arg2;
20112043

20122044
/* Per the POSIX spec, return NaN if either input is NaN */
20132045
if (isnan(arg1) || isnan(arg2))
@@ -2018,8 +2050,14 @@ datan2d(PG_FUNCTION_ARGS)
20182050
/*
20192051
* atan2d maps all inputs to values in the range [-180, 180], so the
20202052
* result should always be finite, even if the inputs are infinite.
2053+
*
2054+
* Note: this coding assumes that atan(1.0) is a suitable scaling constant
2055+
* to get an exact result from atan2(). This might well fail on us at
2056+
* some point, requiring us to decide exactly what inputs we think we're
2057+
* going to guarantee an exact result for.
20212058
*/
2022-
result = (atan2(arg1, arg2) / atan_1_0) * 45.0;
2059+
atan2_arg1_arg2 = atan2(arg1, arg2);
2060+
result = (atan2_arg1_arg2 / atan_1_0) * 45.0;
20232061

20242062
CHECKFLOATVAL(result, false, true);
20252063
PG_RETURN_FLOAT8(result);
@@ -2034,7 +2072,9 @@ datan2d(PG_FUNCTION_ARGS)
20342072
static double
20352073
sind_0_to_30(double x)
20362074
{
2037-
return (sin(x * RADIANS_PER_DEGREE) / sin_30) / 2.0;
2075+
volatile float8 sin_x = sin(x * RADIANS_PER_DEGREE);
2076+
2077+
return (sin_x / sin_30) / 2.0;
20382078
}
20392079

20402080

@@ -2046,7 +2086,7 @@ sind_0_to_30(double x)
20462086
static double
20472087
cosd_0_to_60(double x)
20482088
{
2049-
float8 one_minus_cos_x = 1.0 - cos(x * RADIANS_PER_DEGREE);
2089+
volatile float8 one_minus_cos_x = 1.0 - cos(x * RADIANS_PER_DEGREE);
20502090

20512091
return 1.0 - (one_minus_cos_x / one_minus_cos_60) / 2.0;
20522092
}
@@ -2153,6 +2193,7 @@ dcotd(PG_FUNCTION_ARGS)
21532193
{
21542194
float8 arg1 = PG_GETARG_FLOAT8(0);
21552195
float8 result;
2196+
volatile float8 cot_arg1;
21562197
int sign = 1;
21572198

21582199
/*
@@ -2193,7 +2234,8 @@ dcotd(PG_FUNCTION_ARGS)
21932234
sign = -sign;
21942235
}
21952236

2196-
result = sign * ((cosd_q1(arg1) / sind_q1(arg1)) / cot_45);
2237+
cot_arg1 = cosd_q1(arg1) / sind_q1(arg1);
2238+
result = sign * (cot_arg1 / cot_45);
21972239

21982240
/*
21992241
* On some machines we get cotd(270) = minus zero, but this isn't always
@@ -2270,6 +2312,7 @@ dtand(PG_FUNCTION_ARGS)
22702312
{
22712313
float8 arg1 = PG_GETARG_FLOAT8(0);
22722314
float8 result;
2315+
volatile float8 tan_arg1;
22732316
int sign = 1;
22742317

22752318
/*
@@ -2310,7 +2353,8 @@ dtand(PG_FUNCTION_ARGS)
23102353
sign = -sign;
23112354
}
23122355

2313-
result = sign * ((sind_q1(arg1) / cosd_q1(arg1)) / tan_45);
2356+
tan_arg1 = sind_q1(arg1) / cosd_q1(arg1);
2357+
result = sign * (tan_arg1 / tan_45);
23142358

23152359
/*
23162360
* On some machines we get tand(180) = minus zero, but this isn't always

0 commit comments

Comments
 (0)