Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * float.c
4 : : * Functions for the built-in floating-point types.
5 : : *
6 : : * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
7 : : * Portions Copyright (c) 1994, Regents of the University of California
8 : : *
9 : : *
10 : : * IDENTIFICATION
11 : : * src/backend/utils/adt/float.c
12 : : *
13 : : *-------------------------------------------------------------------------
14 : : */
15 : : #include "postgres.h"
16 : :
17 : : #include <ctype.h>
18 : : #include <float.h>
19 : : #include <math.h>
20 : : #include <limits.h>
21 : :
22 : : #include "catalog/pg_type.h"
23 : : #include "common/int.h"
24 : : #include "common/shortest_dec.h"
25 : : #include "libpq/pqformat.h"
26 : : #include "utils/array.h"
27 : : #include "utils/float.h"
28 : : #include "utils/fmgrprotos.h"
29 : : #include "utils/sortsupport.h"
30 : : #include "utils/timestamp.h"
31 : :
32 : :
33 : : /*
34 : : * Configurable GUC parameter
35 : : *
36 : : * If >0, use shortest-decimal format for output; this is both the default and
37 : : * allows for compatibility with clients that explicitly set a value here to
38 : : * get round-trip-accurate results. If 0 or less, then use the old, slow,
39 : : * decimal rounding method.
40 : : */
41 : : int extra_float_digits = 1;
42 : :
43 : : /* Cached constants for degree-based trig functions */
44 : : static bool degree_consts_set = false;
45 : : static float8 sin_30 = 0;
46 : : static float8 one_minus_cos_60 = 0;
47 : : static float8 asin_0_5 = 0;
48 : : static float8 acos_0_5 = 0;
49 : : static float8 atan_1_0 = 0;
50 : : static float8 tan_45 = 0;
51 : : static float8 cot_45 = 0;
52 : :
53 : : /*
54 : : * These are intentionally not static; don't "fix" them. They will never
55 : : * be referenced by other files, much less changed; but we don't want the
56 : : * compiler to know that, else it might try to precompute expressions
57 : : * involving them. See comments for init_degree_constants().
58 : : */
59 : : float8 degree_c_thirty = 30.0;
60 : : float8 degree_c_forty_five = 45.0;
61 : : float8 degree_c_sixty = 60.0;
62 : : float8 degree_c_one_half = 0.5;
63 : : float8 degree_c_one = 1.0;
64 : :
65 : : /* Local function prototypes */
66 : : static double sind_q1(double x);
67 : : static double cosd_q1(double x);
68 : : static void init_degree_constants(void);
69 : :
70 : :
71 : : /*
72 : : * We use these out-of-line ereport() calls to report float overflow,
73 : : * underflow, and zero-divide, because following our usual practice of
74 : : * repeating them at each call site would lead to a lot of code bloat.
75 : : *
76 : : * This does mean that you don't get a useful error location indicator.
77 : : */
78 : : pg_noinline void
1522 tgl@sss.pgh.pa.us 79 :CBC 21 : float_overflow_error(void)
80 : : {
81 [ + - ]: 21 : ereport(ERROR,
82 : : (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
83 : : errmsg("value out of range: overflow")));
84 : : }
85 : :
86 : : pg_noinline void
87 : 12 : float_underflow_error(void)
88 : : {
89 [ + - ]: 12 : ereport(ERROR,
90 : : (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
91 : : errmsg("value out of range: underflow")));
92 : : }
93 : :
94 : : pg_noinline void
95 : 36 : float_zero_divide_error(void)
96 : : {
97 [ + - ]: 36 : ereport(ERROR,
98 : : (errcode(ERRCODE_DIVISION_BY_ZERO),
99 : : errmsg("division by zero")));
100 : : }
101 : :
102 : :
103 : : /*
104 : : * Returns -1 if 'val' represents negative infinity, 1 if 'val'
105 : : * represents (positive) infinity, and 0 otherwise. On some platforms,
106 : : * this is equivalent to the isinf() macro, but not everywhere: C99
107 : : * does not specify that isinf() needs to distinguish between positive
108 : : * and negative infinity.
109 : : */
110 : : int
7336 neilc@samurai.com 111 :UBC 0 : is_infinite(double val)
112 : : {
7168 bruce@momjian.us 113 [ # # # # ]: 0 : int inf = isinf(val);
114 : :
7336 neilc@samurai.com 115 [ # # ]: 0 : if (inf == 0)
116 : 0 : return 0;
6312 bruce@momjian.us 117 [ # # ]: 0 : else if (val > 0)
7336 neilc@samurai.com 118 : 0 : return 1;
119 : : else
6312 bruce@momjian.us 120 : 0 : return -1;
121 : : }
122 : :
123 : :
124 : : /* ========== USER I/O ROUTINES ========== */
125 : :
126 : :
127 : : /*
128 : : * float4in - converts "num" to float4
129 : : *
130 : : * Note that this code now uses strtof(), where it used to use strtod().
131 : : *
132 : : * The motivation for using strtof() is to avoid a double-rounding problem:
133 : : * for certain decimal inputs, if you round the input correctly to a double,
134 : : * and then round the double to a float, the result is incorrect in that it
135 : : * does not match the result of rounding the decimal value to float directly.
136 : : *
137 : : * One of the best examples is 7.038531e-26:
138 : : *
139 : : * 0xAE43FDp-107 = 7.03853069185120912085...e-26
140 : : * midpoint 7.03853100000000022281...e-26
141 : : * 0xAE43FEp-107 = 7.03853130814879132477...e-26
142 : : *
143 : : * making 0xAE43FDp-107 the correct float result, but if you do the conversion
144 : : * via a double, you get
145 : : *
146 : : * 0xAE43FD.7FFFFFF8p-107 = 7.03853099999999907487...e-26
147 : : * midpoint 7.03853099999999964884...e-26
148 : : * 0xAE43FD.80000000p-107 = 7.03853100000000022281...e-26
149 : : * 0xAE43FD.80000008p-107 = 7.03853100000000137076...e-26
150 : : *
151 : : * so the value rounds to the double exactly on the midpoint between the two
152 : : * nearest floats, and then rounding again to a float gives the incorrect
153 : : * result of 0xAE43FEp-107.
154 : : *
155 : : */
156 : : Datum
8657 tgl@sss.pgh.pa.us 157 :CBC 263555 : float4in(PG_FUNCTION_ARGS)
158 : : {
159 : 263555 : char *num = PG_GETARG_CSTRING(0);
160 : :
480 andrew@dunslane.net 161 : 263555 : PG_RETURN_FLOAT4(float4in_internal(num, NULL, "real", num,
162 : : fcinfo->context));
163 : : }
164 : :
165 : : /*
166 : : * float4in_internal - guts of float4in()
167 : : *
168 : : * This is exposed for use by functions that want a reasonably
169 : : * platform-independent way of inputting floats. The behavior is
170 : : * essentially like strtof + ereturn on error.
171 : : *
172 : : * Uses the same API as float8in_internal below, so most of its
173 : : * comments also apply here, except regarding use in geometric types.
174 : : */
175 : : float4
176 : 268729 : float4in_internal(char *num, char **endptr_p,
177 : : const char *type_name, const char *orig_string,
178 : : struct Node *escontext)
179 : : {
180 : : float val;
181 : : char *endptr;
182 : :
183 : : /*
184 : : * endptr points to the first character _after_ the sequence we recognized
185 : : * as a valid floating point number. orig_string points to the original
186 : : * input string.
187 : : */
188 : :
189 : : /* skip leading whitespace */
3907 tgl@sss.pgh.pa.us 190 [ + + + + ]: 268834 : while (*num != '\0' && isspace((unsigned char) *num))
191 : 105 : num++;
192 : :
193 : : /*
194 : : * Check for an empty-string input to begin with, to avoid the vagaries of
195 : : * strtod() on different platforms.
196 : : */
7336 neilc@samurai.com 197 [ + + ]: 268729 : if (*num == '\0')
480 andrew@dunslane.net 198 [ + - ]: 6 : ereturn(escontext, 0,
199 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
200 : : errmsg("invalid input syntax for type %s: \"%s\"",
201 : : type_name, orig_string)));
202 : :
6708 bruce@momjian.us 203 : 268723 : errno = 0;
1887 rhodiumtoad@postgres 204 : 268723 : val = strtof(num, &endptr);
205 : :
206 : : /* did we not see anything that looks like a double? */
7318 tgl@sss.pgh.pa.us 207 [ + + + + ]: 268723 : if (endptr == num || errno != 0)
208 : : {
4326 bruce@momjian.us 209 : 49 : int save_errno = errno;
210 : :
211 : : /*
212 : : * C99 requires that strtof() accept NaN, [+-]Infinity, and [+-]Inf,
213 : : * but not all platforms support all of these (and some accept them
214 : : * but set ERANGE anyway...) Therefore, we check for these inputs
215 : : * ourselves if strtof() fails.
216 : : *
217 : : * Note: C99 also requires hexadecimal input as well as some extended
218 : : * forms of NaN, but we consider these forms unportable and don't try
219 : : * to support them. You can use 'em if your strtof() takes 'em.
220 : : */
7282 tgl@sss.pgh.pa.us 221 [ - + ]: 49 : if (pg_strncasecmp(num, "NaN", 3) == 0)
222 : : {
7335 tgl@sss.pgh.pa.us 223 :UBC 0 : val = get_float4_nan();
7336 neilc@samurai.com 224 : 0 : endptr = num + 3;
225 : : }
7282 tgl@sss.pgh.pa.us 226 [ - + ]:CBC 49 : else if (pg_strncasecmp(num, "Infinity", 8) == 0)
227 : : {
7335 tgl@sss.pgh.pa.us 228 :UBC 0 : val = get_float4_infinity();
7336 neilc@samurai.com 229 : 0 : endptr = num + 8;
230 : : }
3907 tgl@sss.pgh.pa.us 231 [ - + ]:CBC 49 : else if (pg_strncasecmp(num, "+Infinity", 9) == 0)
232 : : {
3907 tgl@sss.pgh.pa.us 233 :UBC 0 : val = get_float4_infinity();
234 : 0 : endptr = num + 9;
235 : : }
7282 tgl@sss.pgh.pa.us 236 [ - + ]:CBC 49 : else if (pg_strncasecmp(num, "-Infinity", 9) == 0)
237 : : {
7168 bruce@momjian.us 238 :UBC 0 : val = -get_float4_infinity();
7336 neilc@samurai.com 239 : 0 : endptr = num + 9;
240 : : }
3907 tgl@sss.pgh.pa.us 241 [ - + ]:CBC 49 : else if (pg_strncasecmp(num, "inf", 3) == 0)
242 : : {
3907 tgl@sss.pgh.pa.us 243 :UBC 0 : val = get_float4_infinity();
244 : 0 : endptr = num + 3;
245 : : }
3907 tgl@sss.pgh.pa.us 246 [ - + ]:CBC 49 : else if (pg_strncasecmp(num, "+inf", 4) == 0)
247 : : {
3907 tgl@sss.pgh.pa.us 248 :UBC 0 : val = get_float4_infinity();
249 : 0 : endptr = num + 4;
250 : : }
3907 tgl@sss.pgh.pa.us 251 [ - + ]:CBC 49 : else if (pg_strncasecmp(num, "-inf", 4) == 0)
252 : : {
3907 tgl@sss.pgh.pa.us 253 :UBC 0 : val = -get_float4_infinity();
254 : 0 : endptr = num + 4;
255 : : }
4456 tgl@sss.pgh.pa.us 256 [ + + ]:CBC 49 : else if (save_errno == ERANGE)
257 : : {
258 : : /*
259 : : * Some platforms return ERANGE for denormalized numbers (those
260 : : * that are not zero, but are too close to zero to have full
261 : : * precision). We'd prefer not to throw error for that, so try to
262 : : * detect whether it's a "real" out-of-range condition by checking
263 : : * to see if the result is zero or huge.
264 : : */
1887 rhodiumtoad@postgres 265 [ + + + + ]: 33 : if (val == 0.0 ||
266 : : #if !defined(HUGE_VALF)
267 : : isinf(val)
268 : : #else
269 [ + - ]: 6 : (val >= HUGE_VALF || val <= -HUGE_VALF)
270 : : #endif
271 : : )
272 : : {
273 : : /* see comments in float8in_internal for rationale */
480 andrew@dunslane.net 274 : 33 : char *errnumber = pstrdup(num);
275 : :
276 : 33 : errnumber[endptr - num] = '\0';
277 : :
278 [ + + ]: 33 : ereturn(escontext, 0,
279 : : (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
280 : : errmsg("\"%s\" is out of range for type real",
281 : : errnumber)));
282 : : }
283 : : }
284 : : else
285 [ + + ]: 16 : ereturn(escontext, 0,
286 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
287 : : errmsg("invalid input syntax for type %s: \"%s\"",
288 : : type_name, orig_string)));
289 : : }
290 : :
291 : : /* skip trailing whitespace */
7318 tgl@sss.pgh.pa.us 292 [ + + + + ]: 268773 : while (*endptr != '\0' && isspace((unsigned char) *endptr))
7339 neilc@samurai.com 293 : 99 : endptr++;
294 : :
295 : : /* report stopping point if wanted, else complain if not end of string */
480 andrew@dunslane.net 296 [ - + ]: 268674 : if (endptr_p)
480 andrew@dunslane.net 297 :UBC 0 : *endptr_p = endptr;
480 andrew@dunslane.net 298 [ + + ]:CBC 268674 : else if (*endptr != '\0')
299 [ + - ]: 18 : ereturn(escontext, 0,
300 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
301 : : errmsg("invalid input syntax for type %s: \"%s\"",
302 : : type_name, orig_string)));
303 : :
304 : 268656 : return val;
305 : : }
306 : :
307 : : /*
308 : : * float4out - converts a float4 number to a string
309 : : * using a standard output format
310 : : */
311 : : Datum
8657 tgl@sss.pgh.pa.us 312 : 31660 : float4out(PG_FUNCTION_ARGS)
313 : : {
314 : 31660 : float4 num = PG_GETARG_FLOAT4(0);
2015 315 : 31660 : char *ascii = (char *) palloc(32);
316 : 31660 : int ndig = FLT_DIG + extra_float_digits;
317 : :
1887 rhodiumtoad@postgres 318 [ + + ]: 31660 : if (extra_float_digits > 0)
319 : : {
320 : 25266 : float_to_shortest_decimal_buf(num, ascii);
321 : 25266 : PG_RETURN_CSTRING(ascii);
322 : : }
323 : :
2015 tgl@sss.pgh.pa.us 324 : 6394 : (void) pg_strfromd(ascii, 32, ndig, num);
8657 325 : 6394 : PG_RETURN_CSTRING(ascii);
326 : : }
327 : :
328 : : /*
329 : : * float4recv - converts external binary format to float4
330 : : */
331 : : Datum
7646 tgl@sss.pgh.pa.us 332 :UBC 0 : float4recv(PG_FUNCTION_ARGS)
333 : : {
334 : 0 : StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
335 : :
336 : 0 : PG_RETURN_FLOAT4(pq_getmsgfloat4(buf));
337 : : }
338 : :
339 : : /*
340 : : * float4send - converts float4 to binary format
341 : : */
342 : : Datum
7646 tgl@sss.pgh.pa.us 343 :CBC 3246 : float4send(PG_FUNCTION_ARGS)
344 : : {
345 : 3246 : float4 num = PG_GETARG_FLOAT4(0);
346 : : StringInfoData buf;
347 : :
348 : 3246 : pq_begintypsend(&buf);
349 : 3246 : pq_sendfloat4(&buf, num);
350 : 3246 : PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
351 : : }
352 : :
353 : : /*
354 : : * float8in - converts "num" to float8
355 : : */
356 : : Datum
8657 357 : 338538 : float8in(PG_FUNCTION_ARGS)
358 : : {
359 : 338538 : char *num = PG_GETARG_CSTRING(0);
360 : :
492 361 : 338538 : PG_RETURN_FLOAT8(float8in_internal(num, NULL, "double precision", num,
362 : : fcinfo->context));
363 : : }
364 : :
365 : : /*
366 : : * float8in_internal - guts of float8in()
367 : : *
368 : : * This is exposed for use by functions that want a reasonably
369 : : * platform-independent way of inputting doubles. The behavior is
370 : : * essentially like strtod + ereturn on error, but note the following
371 : : * differences:
372 : : * 1. Both leading and trailing whitespace are skipped.
373 : : * 2. If endptr_p is NULL, we report error if there's trailing junk.
374 : : * Otherwise, it's up to the caller to complain about trailing junk.
375 : : * 3. In event of a syntax error, the report mentions the given type_name
376 : : * and prints orig_string as the input; this is meant to support use of
377 : : * this function with types such as "box" and "point", where what we are
378 : : * parsing here is just a substring of orig_string.
379 : : *
380 : : * If escontext points to an ErrorSaveContext node, that is filled instead
381 : : * of throwing an error; the caller must check SOFT_ERROR_OCCURRED()
382 : : * to detect errors.
383 : : *
384 : : * "num" could validly be declared "const char *", but that results in an
385 : : * unreasonable amount of extra casting both here and in callers, so we don't.
386 : : */
387 : : float8
388 : 464400 : float8in_internal(char *num, char **endptr_p,
389 : : const char *type_name, const char *orig_string,
390 : : struct Node *escontext)
391 : : {
392 : : double val;
393 : : char *endptr;
394 : :
395 : : /* skip leading whitespace */
3907 396 [ + + + + ]: 465062 : while (*num != '\0' && isspace((unsigned char) *num))
397 : 662 : num++;
398 : :
399 : : /*
400 : : * Check for an empty-string input to begin with, to avoid the vagaries of
401 : : * strtod() on different platforms.
402 : : */
7336 neilc@samurai.com 403 [ + + ]: 464400 : if (*num == '\0')
492 tgl@sss.pgh.pa.us 404 [ + - ]: 9 : ereturn(escontext, 0,
405 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
406 : : errmsg("invalid input syntax for type %s: \"%s\"",
407 : : type_name, orig_string)));
408 : :
6708 bruce@momjian.us 409 : 464391 : errno = 0;
9716 410 : 464391 : val = strtod(num, &endptr);
411 : :
412 : : /* did we not see anything that looks like a double? */
7318 tgl@sss.pgh.pa.us 413 [ + + + + ]: 464391 : if (endptr == num || errno != 0)
414 : : {
4326 bruce@momjian.us 415 : 135 : int save_errno = errno;
416 : :
417 : : /*
418 : : * C99 requires that strtod() accept NaN, [+-]Infinity, and [+-]Inf,
419 : : * but not all platforms support all of these (and some accept them
420 : : * but set ERANGE anyway...) Therefore, we check for these inputs
421 : : * ourselves if strtod() fails.
422 : : *
423 : : * Note: C99 also requires hexadecimal input as well as some extended
424 : : * forms of NaN, but we consider these forms unportable and don't try
425 : : * to support them. You can use 'em if your strtod() takes 'em.
426 : : */
7282 tgl@sss.pgh.pa.us 427 [ - + ]: 135 : if (pg_strncasecmp(num, "NaN", 3) == 0)
428 : : {
7335 tgl@sss.pgh.pa.us 429 :UBC 0 : val = get_float8_nan();
7336 neilc@samurai.com 430 : 0 : endptr = num + 3;
431 : : }
7282 tgl@sss.pgh.pa.us 432 [ - + ]:CBC 135 : else if (pg_strncasecmp(num, "Infinity", 8) == 0)
433 : : {
7335 tgl@sss.pgh.pa.us 434 :UBC 0 : val = get_float8_infinity();
7336 neilc@samurai.com 435 : 0 : endptr = num + 8;
436 : : }
3907 tgl@sss.pgh.pa.us 437 [ - + ]:CBC 135 : else if (pg_strncasecmp(num, "+Infinity", 9) == 0)
438 : : {
3907 tgl@sss.pgh.pa.us 439 :UBC 0 : val = get_float8_infinity();
440 : 0 : endptr = num + 9;
441 : : }
7282 tgl@sss.pgh.pa.us 442 [ - + ]:CBC 135 : else if (pg_strncasecmp(num, "-Infinity", 9) == 0)
443 : : {
7168 bruce@momjian.us 444 :UBC 0 : val = -get_float8_infinity();
7336 neilc@samurai.com 445 : 0 : endptr = num + 9;
446 : : }
3907 tgl@sss.pgh.pa.us 447 [ - + ]:CBC 135 : else if (pg_strncasecmp(num, "inf", 3) == 0)
448 : : {
3907 tgl@sss.pgh.pa.us 449 :UBC 0 : val = get_float8_infinity();
450 : 0 : endptr = num + 3;
451 : : }
3907 tgl@sss.pgh.pa.us 452 [ - + ]:CBC 135 : else if (pg_strncasecmp(num, "+inf", 4) == 0)
453 : : {
3907 tgl@sss.pgh.pa.us 454 :UBC 0 : val = get_float8_infinity();
455 : 0 : endptr = num + 4;
456 : : }
3907 tgl@sss.pgh.pa.us 457 [ - + ]:CBC 135 : else if (pg_strncasecmp(num, "-inf", 4) == 0)
458 : : {
3907 tgl@sss.pgh.pa.us 459 :UBC 0 : val = -get_float8_infinity();
460 : 0 : endptr = num + 4;
461 : : }
4456 tgl@sss.pgh.pa.us 462 [ + + ]:CBC 135 : else if (save_errno == ERANGE)
463 : : {
464 : : /*
465 : : * Some platforms return ERANGE for denormalized numbers (those
466 : : * that are not zero, but are too close to zero to have full
467 : : * precision). We'd prefer not to throw error for that, so try to
468 : : * detect whether it's a "real" out-of-range condition by checking
469 : : * to see if the result is zero or huge.
470 : : *
471 : : * On error, we intentionally complain about double precision not
472 : : * the given type name, and we print only the part of the string
473 : : * that is the current number.
474 : : */
475 [ + + + + : 63 : if (val == 0.0 || val >= HUGE_VAL || val <= -HUGE_VAL)
+ + ]
476 : : {
2937 477 : 54 : char *errnumber = pstrdup(num);
478 : :
479 : 54 : errnumber[endptr - num] = '\0';
492 480 [ + + ]: 54 : ereturn(escontext, 0,
481 : : (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
482 : : errmsg("\"%s\" is out of range for type double precision",
483 : : errnumber)));
484 : : }
485 : : }
486 : : else
487 [ + + ]: 72 : ereturn(escontext, 0,
488 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
489 : : errmsg("invalid input syntax for type %s: \"%s\"",
490 : : type_name, orig_string)));
491 : : }
492 : :
493 : : /* skip trailing whitespace */
7318 494 [ + + + + ]: 464467 : while (*endptr != '\0' && isspace((unsigned char) *endptr))
7339 neilc@samurai.com 495 : 202 : endptr++;
496 : :
497 : : /* report stopping point if wanted, else complain if not end of string */
2937 tgl@sss.pgh.pa.us 498 [ + + ]: 464265 : if (endptr_p)
499 : 125763 : *endptr_p = endptr;
500 [ + + ]: 338502 : else if (*endptr != '\0')
492 501 [ + + ]: 21 : ereturn(escontext, 0,
502 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
503 : : errmsg("invalid input syntax for type %s: \"%s\"",
504 : : type_name, orig_string)));
505 : :
2937 506 : 464244 : return val;
507 : : }
508 : :
509 : :
510 : : /*
511 : : * float8out - converts float8 number to a string
512 : : * using a standard output format
513 : : */
514 : : Datum
8657 515 : 380000 : float8out(PG_FUNCTION_ARGS)
516 : : {
517 : 380000 : float8 num = PG_GETARG_FLOAT8(0);
518 : :
2937 519 : 380000 : PG_RETURN_CSTRING(float8out_internal(num));
520 : : }
521 : :
522 : : /*
523 : : * float8out_internal - guts of float8out()
524 : : *
525 : : * This is exposed for use by functions that want a reasonably
526 : : * platform-independent way of outputting doubles.
527 : : * The result is always palloc'd.
528 : : */
529 : : char *
530 : 2181270 : float8out_internal(double num)
531 : : {
2015 532 : 2181270 : char *ascii = (char *) palloc(32);
533 : 2181270 : int ndig = DBL_DIG + extra_float_digits;
534 : :
1887 rhodiumtoad@postgres 535 [ + + ]: 2181270 : if (extra_float_digits > 0)
536 : : {
537 : 2072649 : double_to_shortest_decimal_buf(num, ascii);
538 : 2072649 : return ascii;
539 : : }
540 : :
2015 tgl@sss.pgh.pa.us 541 : 108621 : (void) pg_strfromd(ascii, 32, ndig, num);
2937 542 : 108621 : return ascii;
543 : : }
544 : :
545 : : /*
546 : : * float8recv - converts external binary format to float8
547 : : */
548 : : Datum
7646 549 : 13 : float8recv(PG_FUNCTION_ARGS)
550 : : {
551 : 13 : StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
552 : :
553 : 13 : PG_RETURN_FLOAT8(pq_getmsgfloat8(buf));
554 : : }
555 : :
556 : : /*
557 : : * float8send - converts float8 to binary format
558 : : */
559 : : Datum
560 : 2578 : float8send(PG_FUNCTION_ARGS)
561 : : {
562 : 2578 : float8 num = PG_GETARG_FLOAT8(0);
563 : : StringInfoData buf;
564 : :
565 : 2578 : pq_begintypsend(&buf);
566 : 2578 : pq_sendfloat8(&buf, num);
567 : 2578 : PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
568 : : }
569 : :
570 : :
571 : : /* ========== PUBLIC ROUTINES ========== */
572 : :
573 : :
574 : : /*
575 : : * ======================
576 : : * FLOAT4 BASE OPERATIONS
577 : : * ======================
578 : : */
579 : :
580 : : /*
581 : : * float4abs - returns |arg1| (absolute value)
582 : : */
583 : : Datum
8657 584 : 15 : float4abs(PG_FUNCTION_ARGS)
585 : : {
586 : 15 : float4 arg1 = PG_GETARG_FLOAT4(0);
587 : :
554 peter@eisentraut.org 588 : 15 : PG_RETURN_FLOAT4(fabsf(arg1));
589 : : }
590 : :
591 : : /*
592 : : * float4um - returns -arg1 (unary minus)
593 : : */
594 : : Datum
8657 tgl@sss.pgh.pa.us 595 : 8 : float4um(PG_FUNCTION_ARGS)
596 : : {
597 : 8 : float4 arg1 = PG_GETARG_FLOAT4(0);
598 : : float4 result;
599 : :
5534 600 : 8 : result = -arg1;
6312 bruce@momjian.us 601 : 8 : PG_RETURN_FLOAT4(result);
602 : : }
603 : :
604 : : Datum
8347 bruce@momjian.us 605 :UBC 0 : float4up(PG_FUNCTION_ARGS)
606 : : {
607 : 0 : float4 arg = PG_GETARG_FLOAT4(0);
608 : :
609 : 0 : PG_RETURN_FLOAT4(arg);
610 : : }
611 : :
612 : : Datum
8657 tgl@sss.pgh.pa.us 613 :CBC 9 : float4larger(PG_FUNCTION_ARGS)
614 : : {
615 : 9 : float4 arg1 = PG_GETARG_FLOAT4(0);
616 : 9 : float4 arg2 = PG_GETARG_FLOAT4(1);
617 : : float4 result;
618 : :
2086 tomas.vondra@postgre 619 [ + + ]: 9 : if (float4_gt(arg1, arg2))
7564 tgl@sss.pgh.pa.us 620 : 3 : result = arg1;
621 : : else
622 : 6 : result = arg2;
8657 623 : 9 : PG_RETURN_FLOAT4(result);
624 : : }
625 : :
626 : : Datum
8657 tgl@sss.pgh.pa.us 627 :UBC 0 : float4smaller(PG_FUNCTION_ARGS)
628 : : {
629 : 0 : float4 arg1 = PG_GETARG_FLOAT4(0);
630 : 0 : float4 arg2 = PG_GETARG_FLOAT4(1);
631 : : float4 result;
632 : :
2086 tomas.vondra@postgre 633 [ # # ]: 0 : if (float4_lt(arg1, arg2))
7564 tgl@sss.pgh.pa.us 634 : 0 : result = arg1;
635 : : else
636 : 0 : result = arg2;
8657 637 : 0 : PG_RETURN_FLOAT4(result);
638 : : }
639 : :
640 : : /*
641 : : * ======================
642 : : * FLOAT8 BASE OPERATIONS
643 : : * ======================
644 : : */
645 : :
646 : : /*
647 : : * float8abs - returns |arg1| (absolute value)
648 : : */
649 : : Datum
8657 tgl@sss.pgh.pa.us 650 :CBC 56216 : float8abs(PG_FUNCTION_ARGS)
651 : : {
652 : 56216 : float8 arg1 = PG_GETARG_FLOAT8(0);
653 : :
6312 bruce@momjian.us 654 : 56216 : PG_RETURN_FLOAT8(fabs(arg1));
655 : : }
656 : :
657 : :
658 : : /*
659 : : * float8um - returns -arg1 (unary minus)
660 : : */
661 : : Datum
8657 tgl@sss.pgh.pa.us 662 : 161 : float8um(PG_FUNCTION_ARGS)
663 : : {
664 : 161 : float8 arg1 = PG_GETARG_FLOAT8(0);
665 : : float8 result;
666 : :
5534 667 : 161 : result = -arg1;
8657 668 : 161 : PG_RETURN_FLOAT8(result);
669 : : }
670 : :
671 : : Datum
8347 bruce@momjian.us 672 :UBC 0 : float8up(PG_FUNCTION_ARGS)
673 : : {
674 : 0 : float8 arg = PG_GETARG_FLOAT8(0);
675 : :
676 : 0 : PG_RETURN_FLOAT8(arg);
677 : : }
678 : :
679 : : Datum
8657 tgl@sss.pgh.pa.us 680 :CBC 6438 : float8larger(PG_FUNCTION_ARGS)
681 : : {
682 : 6438 : float8 arg1 = PG_GETARG_FLOAT8(0);
683 : 6438 : float8 arg2 = PG_GETARG_FLOAT8(1);
684 : : float8 result;
685 : :
2086 tomas.vondra@postgre 686 [ + + ]: 6438 : if (float8_gt(arg1, arg2))
7564 tgl@sss.pgh.pa.us 687 : 6193 : result = arg1;
688 : : else
689 : 245 : result = arg2;
8657 690 : 6438 : PG_RETURN_FLOAT8(result);
691 : : }
692 : :
693 : : Datum
694 : 576 : float8smaller(PG_FUNCTION_ARGS)
695 : : {
696 : 576 : float8 arg1 = PG_GETARG_FLOAT8(0);
697 : 576 : float8 arg2 = PG_GETARG_FLOAT8(1);
698 : : float8 result;
699 : :
2086 tomas.vondra@postgre 700 [ + + ]: 576 : if (float8_lt(arg1, arg2))
7564 tgl@sss.pgh.pa.us 701 : 444 : result = arg1;
702 : : else
703 : 132 : result = arg2;
8657 704 : 576 : PG_RETURN_FLOAT8(result);
705 : : }
706 : :
707 : :
708 : : /*
709 : : * ====================
710 : : * ARITHMETIC OPERATORS
711 : : * ====================
712 : : */
713 : :
714 : : /*
715 : : * float4pl - returns arg1 + arg2
716 : : * float4mi - returns arg1 - arg2
717 : : * float4mul - returns arg1 * arg2
718 : : * float4div - returns arg1 / arg2
719 : : */
720 : : Datum
721 : 27 : float4pl(PG_FUNCTION_ARGS)
722 : : {
5534 723 : 27 : float4 arg1 = PG_GETARG_FLOAT4(0);
724 : 27 : float4 arg2 = PG_GETARG_FLOAT4(1);
725 : :
2086 tomas.vondra@postgre 726 : 27 : PG_RETURN_FLOAT4(float4_pl(arg1, arg2));
727 : : }
728 : :
729 : : Datum
8657 tgl@sss.pgh.pa.us 730 : 9 : float4mi(PG_FUNCTION_ARGS)
731 : : {
732 : 9 : float4 arg1 = PG_GETARG_FLOAT4(0);
733 : 9 : float4 arg2 = PG_GETARG_FLOAT4(1);
734 : :
2086 tomas.vondra@postgre 735 : 9 : PG_RETURN_FLOAT4(float4_mi(arg1, arg2));
736 : : }
737 : :
738 : : Datum
8657 tgl@sss.pgh.pa.us 739 : 18 : float4mul(PG_FUNCTION_ARGS)
740 : : {
741 : 18 : float4 arg1 = PG_GETARG_FLOAT4(0);
742 : 18 : float4 arg2 = PG_GETARG_FLOAT4(1);
743 : :
2086 tomas.vondra@postgre 744 : 18 : PG_RETURN_FLOAT4(float4_mul(arg1, arg2));
745 : : }
746 : :
747 : : Datum
8657 tgl@sss.pgh.pa.us 748 : 24 : float4div(PG_FUNCTION_ARGS)
749 : : {
750 : 24 : float4 arg1 = PG_GETARG_FLOAT4(0);
751 : 24 : float4 arg2 = PG_GETARG_FLOAT4(1);
752 : :
2086 tomas.vondra@postgre 753 : 24 : PG_RETURN_FLOAT4(float4_div(arg1, arg2));
754 : : }
755 : :
756 : : /*
757 : : * float8pl - returns arg1 + arg2
758 : : * float8mi - returns arg1 - arg2
759 : : * float8mul - returns arg1 * arg2
760 : : * float8div - returns arg1 / arg2
761 : : */
762 : : Datum
8657 tgl@sss.pgh.pa.us 763 : 47627 : float8pl(PG_FUNCTION_ARGS)
764 : : {
765 : 47627 : float8 arg1 = PG_GETARG_FLOAT8(0);
766 : 47627 : float8 arg2 = PG_GETARG_FLOAT8(1);
767 : :
2086 tomas.vondra@postgre 768 : 47627 : PG_RETURN_FLOAT8(float8_pl(arg1, arg2));
769 : : }
770 : :
771 : : Datum
8657 tgl@sss.pgh.pa.us 772 : 6276 : float8mi(PG_FUNCTION_ARGS)
773 : : {
774 : 6276 : float8 arg1 = PG_GETARG_FLOAT8(0);
775 : 6276 : float8 arg2 = PG_GETARG_FLOAT8(1);
776 : :
2086 tomas.vondra@postgre 777 : 6276 : PG_RETURN_FLOAT8(float8_mi(arg1, arg2));
778 : : }
779 : :
780 : : Datum
8657 tgl@sss.pgh.pa.us 781 : 324062 : float8mul(PG_FUNCTION_ARGS)
782 : : {
783 : 324062 : float8 arg1 = PG_GETARG_FLOAT8(0);
784 : 324062 : float8 arg2 = PG_GETARG_FLOAT8(1);
785 : :
2086 tomas.vondra@postgre 786 : 324062 : PG_RETURN_FLOAT8(float8_mul(arg1, arg2));
787 : : }
788 : :
789 : : Datum
8657 tgl@sss.pgh.pa.us 790 : 7634 : float8div(PG_FUNCTION_ARGS)
791 : : {
792 : 7634 : float8 arg1 = PG_GETARG_FLOAT8(0);
793 : 7634 : float8 arg2 = PG_GETARG_FLOAT8(1);
794 : :
2086 tomas.vondra@postgre 795 : 7634 : PG_RETURN_FLOAT8(float8_div(arg1, arg2));
796 : : }
797 : :
798 : :
799 : : /*
800 : : * ====================
801 : : * COMPARISON OPERATORS
802 : : * ====================
803 : : */
804 : :
805 : : /*
806 : : * float4{eq,ne,lt,le,gt,ge} - float4/float4 comparison operations
807 : : */
808 : : int
8382 tgl@sss.pgh.pa.us 809 : 5887495 : float4_cmp_internal(float4 a, float4 b)
810 : : {
2086 tomas.vondra@postgre 811 [ + + ]: 5887495 : if (float4_gt(a, b))
812 : 128844 : return 1;
813 [ + + ]: 5758651 : if (float4_lt(a, b))
814 : 1010552 : return -1;
815 : 4748099 : return 0;
816 : : }
817 : :
818 : : Datum
8657 tgl@sss.pgh.pa.us 819 : 31190 : float4eq(PG_FUNCTION_ARGS)
820 : : {
821 : 31190 : float4 arg1 = PG_GETARG_FLOAT4(0);
822 : 31190 : float4 arg2 = PG_GETARG_FLOAT4(1);
823 : :
2086 tomas.vondra@postgre 824 : 31190 : PG_RETURN_BOOL(float4_eq(arg1, arg2));
825 : : }
826 : :
827 : : Datum
8657 tgl@sss.pgh.pa.us 828 : 15 : float4ne(PG_FUNCTION_ARGS)
829 : : {
830 : 15 : float4 arg1 = PG_GETARG_FLOAT4(0);
831 : 15 : float4 arg2 = PG_GETARG_FLOAT4(1);
832 : :
2086 tomas.vondra@postgre 833 : 15 : PG_RETURN_BOOL(float4_ne(arg1, arg2));
834 : : }
835 : :
836 : : Datum
8657 tgl@sss.pgh.pa.us 837 : 37044 : float4lt(PG_FUNCTION_ARGS)
838 : : {
839 : 37044 : float4 arg1 = PG_GETARG_FLOAT4(0);
840 : 37044 : float4 arg2 = PG_GETARG_FLOAT4(1);
841 : :
2086 tomas.vondra@postgre 842 : 37044 : PG_RETURN_BOOL(float4_lt(arg1, arg2));
843 : : }
844 : :
845 : : Datum
8657 tgl@sss.pgh.pa.us 846 : 1914 : float4le(PG_FUNCTION_ARGS)
847 : : {
848 : 1914 : float4 arg1 = PG_GETARG_FLOAT4(0);
849 : 1914 : float4 arg2 = PG_GETARG_FLOAT4(1);
850 : :
2086 tomas.vondra@postgre 851 : 1914 : PG_RETURN_BOOL(float4_le(arg1, arg2));
852 : : }
853 : :
854 : : Datum
8657 tgl@sss.pgh.pa.us 855 : 2319 : float4gt(PG_FUNCTION_ARGS)
856 : : {
857 : 2319 : float4 arg1 = PG_GETARG_FLOAT4(0);
858 : 2319 : float4 arg2 = PG_GETARG_FLOAT4(1);
859 : :
2086 tomas.vondra@postgre 860 : 2319 : PG_RETURN_BOOL(float4_gt(arg1, arg2));
861 : : }
862 : :
863 : : Datum
8657 tgl@sss.pgh.pa.us 864 : 1914 : float4ge(PG_FUNCTION_ARGS)
865 : : {
866 : 1914 : float4 arg1 = PG_GETARG_FLOAT4(0);
867 : 1914 : float4 arg2 = PG_GETARG_FLOAT4(1);
868 : :
2086 tomas.vondra@postgre 869 : 1914 : PG_RETURN_BOOL(float4_ge(arg1, arg2));
870 : : }
871 : :
872 : : Datum
8382 tgl@sss.pgh.pa.us 873 : 947381 : btfloat4cmp(PG_FUNCTION_ARGS)
874 : : {
875 : 947381 : float4 arg1 = PG_GETARG_FLOAT4(0);
876 : 947381 : float4 arg2 = PG_GETARG_FLOAT4(1);
877 : :
878 : 947381 : PG_RETURN_INT32(float4_cmp_internal(arg1, arg2));
879 : : }
880 : :
881 : : static int
4512 882 : 4940114 : btfloat4fastcmp(Datum x, Datum y, SortSupport ssup)
883 : : {
884 : 4940114 : float4 arg1 = DatumGetFloat4(x);
885 : 4940114 : float4 arg2 = DatumGetFloat4(y);
886 : :
887 : 4940114 : return float4_cmp_internal(arg1, arg2);
888 : : }
889 : :
890 : : Datum
891 : 481 : btfloat4sortsupport(PG_FUNCTION_ARGS)
892 : : {
4326 bruce@momjian.us 893 : 481 : SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0);
894 : :
4512 tgl@sss.pgh.pa.us 895 : 481 : ssup->comparator = btfloat4fastcmp;
896 : 481 : PG_RETURN_VOID();
897 : : }
898 : :
899 : : /*
900 : : * float8{eq,ne,lt,le,gt,ge} - float8/float8 comparison operations
901 : : */
902 : : int
8382 903 : 12227662 : float8_cmp_internal(float8 a, float8 b)
904 : : {
2086 tomas.vondra@postgre 905 [ + + ]: 12227662 : if (float8_gt(a, b))
906 : 4481472 : return 1;
907 [ + + ]: 7746190 : if (float8_lt(a, b))
908 : 7618686 : return -1;
909 : 127504 : return 0;
910 : : }
911 : :
912 : : Datum
8657 tgl@sss.pgh.pa.us 913 : 295978 : float8eq(PG_FUNCTION_ARGS)
914 : : {
915 : 295978 : float8 arg1 = PG_GETARG_FLOAT8(0);
916 : 295978 : float8 arg2 = PG_GETARG_FLOAT8(1);
917 : :
2086 tomas.vondra@postgre 918 : 295978 : PG_RETURN_BOOL(float8_eq(arg1, arg2));
919 : : }
920 : :
921 : : Datum
8657 tgl@sss.pgh.pa.us 922 : 177 : float8ne(PG_FUNCTION_ARGS)
923 : : {
924 : 177 : float8 arg1 = PG_GETARG_FLOAT8(0);
925 : 177 : float8 arg2 = PG_GETARG_FLOAT8(1);
926 : :
2086 tomas.vondra@postgre 927 : 177 : PG_RETURN_BOOL(float8_ne(arg1, arg2));
928 : : }
929 : :
930 : : Datum
8657 tgl@sss.pgh.pa.us 931 : 83850 : float8lt(PG_FUNCTION_ARGS)
932 : : {
933 : 83850 : float8 arg1 = PG_GETARG_FLOAT8(0);
934 : 83850 : float8 arg2 = PG_GETARG_FLOAT8(1);
935 : :
2086 tomas.vondra@postgre 936 : 83850 : PG_RETURN_BOOL(float8_lt(arg1, arg2));
937 : : }
938 : :
939 : : Datum
8657 tgl@sss.pgh.pa.us 940 : 3016 : float8le(PG_FUNCTION_ARGS)
941 : : {
942 : 3016 : float8 arg1 = PG_GETARG_FLOAT8(0);
943 : 3016 : float8 arg2 = PG_GETARG_FLOAT8(1);
944 : :
2086 tomas.vondra@postgre 945 : 3016 : PG_RETURN_BOOL(float8_le(arg1, arg2));
946 : : }
947 : :
948 : : Datum
8657 tgl@sss.pgh.pa.us 949 : 15427 : float8gt(PG_FUNCTION_ARGS)
950 : : {
951 : 15427 : float8 arg1 = PG_GETARG_FLOAT8(0);
952 : 15427 : float8 arg2 = PG_GETARG_FLOAT8(1);
953 : :
2086 tomas.vondra@postgre 954 : 15427 : PG_RETURN_BOOL(float8_gt(arg1, arg2));
955 : : }
956 : :
957 : : Datum
8657 tgl@sss.pgh.pa.us 958 : 10652 : float8ge(PG_FUNCTION_ARGS)
959 : : {
960 : 10652 : float8 arg1 = PG_GETARG_FLOAT8(0);
961 : 10652 : float8 arg2 = PG_GETARG_FLOAT8(1);
962 : :
2086 tomas.vondra@postgre 963 : 10652 : PG_RETURN_BOOL(float8_ge(arg1, arg2));
964 : : }
965 : :
966 : : Datum
8382 tgl@sss.pgh.pa.us 967 : 1646 : btfloat8cmp(PG_FUNCTION_ARGS)
968 : : {
969 : 1646 : float8 arg1 = PG_GETARG_FLOAT8(0);
970 : 1646 : float8 arg2 = PG_GETARG_FLOAT8(1);
971 : :
972 : 1646 : PG_RETURN_INT32(float8_cmp_internal(arg1, arg2));
973 : : }
974 : :
975 : : static int
4512 976 : 3532250 : btfloat8fastcmp(Datum x, Datum y, SortSupport ssup)
977 : : {
978 : 3532250 : float8 arg1 = DatumGetFloat8(x);
979 : 3532250 : float8 arg2 = DatumGetFloat8(y);
980 : :
981 : 3532250 : return float8_cmp_internal(arg1, arg2);
982 : : }
983 : :
984 : : Datum
985 : 486 : btfloat8sortsupport(PG_FUNCTION_ARGS)
986 : : {
4326 bruce@momjian.us 987 : 486 : SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0);
988 : :
4512 tgl@sss.pgh.pa.us 989 : 486 : ssup->comparator = btfloat8fastcmp;
990 : 486 : PG_RETURN_VOID();
991 : : }
992 : :
993 : : Datum
7459 tgl@sss.pgh.pa.us 994 :UBC 0 : btfloat48cmp(PG_FUNCTION_ARGS)
995 : : {
996 : 0 : float4 arg1 = PG_GETARG_FLOAT4(0);
997 : 0 : float8 arg2 = PG_GETARG_FLOAT8(1);
998 : :
999 : : /* widen float4 to float8 and then compare */
1000 : 0 : PG_RETURN_INT32(float8_cmp_internal(arg1, arg2));
1001 : : }
1002 : :
1003 : : Datum
1004 : 0 : btfloat84cmp(PG_FUNCTION_ARGS)
1005 : : {
1006 : 0 : float8 arg1 = PG_GETARG_FLOAT8(0);
1007 : 0 : float4 arg2 = PG_GETARG_FLOAT4(1);
1008 : :
1009 : : /* widen float4 to float8 and then compare */
1010 : 0 : PG_RETURN_INT32(float8_cmp_internal(arg1, arg2));
1011 : : }
1012 : :
1013 : : /*
1014 : : * in_range support function for float8.
1015 : : *
1016 : : * Note: we needn't supply a float8_float4 variant, as implicit coercion
1017 : : * of the offset value takes care of that scenario just as well.
1018 : : */
1019 : : Datum
2241 tgl@sss.pgh.pa.us 1020 :CBC 576 : in_range_float8_float8(PG_FUNCTION_ARGS)
1021 : : {
1022 : 576 : float8 val = PG_GETARG_FLOAT8(0);
1023 : 576 : float8 base = PG_GETARG_FLOAT8(1);
1024 : 576 : float8 offset = PG_GETARG_FLOAT8(2);
1025 : 576 : bool sub = PG_GETARG_BOOL(3);
1026 : 576 : bool less = PG_GETARG_BOOL(4);
1027 : : float8 sum;
1028 : :
1029 : : /*
1030 : : * Reject negative or NaN offset. Negative is per spec, and NaN is
1031 : : * because appropriate semantics for that seem non-obvious.
1032 : : */
1033 [ + + - + ]: 576 : if (isnan(offset) || offset < 0)
1034 [ + - ]: 3 : ereport(ERROR,
1035 : : (errcode(ERRCODE_INVALID_PRECEDING_OR_FOLLOWING_SIZE),
1036 : : errmsg("invalid preceding or following size in window function")));
1037 : :
1038 : : /*
1039 : : * Deal with cases where val and/or base is NaN, following the rule that
1040 : : * NaN sorts after non-NaN (cf float8_cmp_internal). The offset cannot
1041 : : * affect the conclusion.
1042 : : */
1043 [ + + ]: 573 : if (isnan(val))
1044 : : {
1045 [ + + ]: 93 : if (isnan(base))
1046 : 30 : PG_RETURN_BOOL(true); /* NAN = NAN */
1047 : : else
1048 : 63 : PG_RETURN_BOOL(!less); /* NAN > non-NAN */
1049 : : }
1050 [ + + ]: 480 : else if (isnan(base))
1051 : : {
1052 : 63 : PG_RETURN_BOOL(less); /* non-NAN < NAN */
1053 : : }
1054 : :
1055 : : /*
1056 : : * Deal with cases where both base and offset are infinite, and computing
1057 : : * base +/- offset would produce NaN. This corresponds to a window frame
1058 : : * whose boundary infinitely precedes +inf or infinitely follows -inf,
1059 : : * which is not well-defined. For consistency with other cases involving
1060 : : * infinities, such as the fact that +inf infinitely follows +inf, we
1061 : : * choose to assume that +inf infinitely precedes +inf and -inf infinitely
1062 : : * follows -inf, and therefore that all finite and infinite values are in
1063 : : * such a window frame.
1064 : : *
1065 : : * offset is known positive, so we need only check the sign of base in
1066 : : * this test.
1067 : : */
1364 1068 [ + + + + : 417 : if (isinf(offset) && isinf(base) &&
+ + + + ]
1069 : : (sub ? base > 0 : base < 0))
1070 : 87 : PG_RETURN_BOOL(true);
1071 : :
1072 : : /*
1073 : : * Otherwise it should be safe to compute base +/- offset. We trust the
1074 : : * FPU to cope if an input is +/-inf or the true sum would overflow, and
1075 : : * produce a suitably signed infinity, which will compare properly against
1076 : : * val whether or not that's infinity.
1077 : : */
2241 1078 [ + + ]: 330 : if (sub)
1079 : 180 : sum = base - offset;
1080 : : else
1081 : 150 : sum = base + offset;
1082 : :
1083 [ + + ]: 330 : if (less)
1084 : 129 : PG_RETURN_BOOL(val <= sum);
1085 : : else
1086 : 201 : PG_RETURN_BOOL(val >= sum);
1087 : : }
1088 : :
1089 : : /*
1090 : : * in_range support function for float4.
1091 : : *
1092 : : * We would need a float4_float8 variant in any case, so we supply that and
1093 : : * let implicit coercion take care of the float4_float4 case.
1094 : : */
1095 : : Datum
1096 : 576 : in_range_float4_float8(PG_FUNCTION_ARGS)
1097 : : {
2171 1098 : 576 : float4 val = PG_GETARG_FLOAT4(0);
1099 : 576 : float4 base = PG_GETARG_FLOAT4(1);
1100 : 576 : float8 offset = PG_GETARG_FLOAT8(2);
1101 : 576 : bool sub = PG_GETARG_BOOL(3);
1102 : 576 : bool less = PG_GETARG_BOOL(4);
1103 : : float8 sum;
1104 : :
1105 : : /*
1106 : : * Reject negative or NaN offset. Negative is per spec, and NaN is
1107 : : * because appropriate semantics for that seem non-obvious.
1108 : : */
1109 [ + + - + ]: 576 : if (isnan(offset) || offset < 0)
1110 [ + - ]: 3 : ereport(ERROR,
1111 : : (errcode(ERRCODE_INVALID_PRECEDING_OR_FOLLOWING_SIZE),
1112 : : errmsg("invalid preceding or following size in window function")));
1113 : :
1114 : : /*
1115 : : * Deal with cases where val and/or base is NaN, following the rule that
1116 : : * NaN sorts after non-NaN (cf float8_cmp_internal). The offset cannot
1117 : : * affect the conclusion.
1118 : : */
1119 [ + + ]: 573 : if (isnan(val))
1120 : : {
1121 [ + + ]: 93 : if (isnan(base))
1122 : 30 : PG_RETURN_BOOL(true); /* NAN = NAN */
1123 : : else
1124 : 63 : PG_RETURN_BOOL(!less); /* NAN > non-NAN */
1125 : : }
1126 [ + + ]: 480 : else if (isnan(base))
1127 : : {
1128 : 63 : PG_RETURN_BOOL(less); /* non-NAN < NAN */
1129 : : }
1130 : :
1131 : : /*
1132 : : * Deal with cases where both base and offset are infinite, and computing
1133 : : * base +/- offset would produce NaN. This corresponds to a window frame
1134 : : * whose boundary infinitely precedes +inf or infinitely follows -inf,
1135 : : * which is not well-defined. For consistency with other cases involving
1136 : : * infinities, such as the fact that +inf infinitely follows +inf, we
1137 : : * choose to assume that +inf infinitely precedes +inf and -inf infinitely
1138 : : * follows -inf, and therefore that all finite and infinite values are in
1139 : : * such a window frame.
1140 : : *
1141 : : * offset is known positive, so we need only check the sign of base in
1142 : : * this test.
1143 : : */
1364 1144 [ + + + + : 417 : if (isinf(offset) && isinf(base) &&
+ + + + ]
1145 : : (sub ? base > 0 : base < 0))
1146 : 87 : PG_RETURN_BOOL(true);
1147 : :
1148 : : /*
1149 : : * Otherwise it should be safe to compute base +/- offset. We trust the
1150 : : * FPU to cope if an input is +/-inf or the true sum would overflow, and
1151 : : * produce a suitably signed infinity, which will compare properly against
1152 : : * val whether or not that's infinity.
1153 : : */
2171 1154 [ + + ]: 330 : if (sub)
1155 : 180 : sum = base - offset;
1156 : : else
1157 : 150 : sum = base + offset;
1158 : :
1159 [ + + ]: 330 : if (less)
1160 : 129 : PG_RETURN_BOOL(val <= sum);
1161 : : else
1162 : 201 : PG_RETURN_BOOL(val >= sum);
1163 : : }
1164 : :
1165 : :
1166 : : /*
1167 : : * ===================
1168 : : * CONVERSION ROUTINES
1169 : : * ===================
1170 : : */
1171 : :
1172 : : /*
1173 : : * ftod - converts a float4 number to a float8 number
1174 : : */
1175 : : Datum
8657 1176 : 147 : ftod(PG_FUNCTION_ARGS)
1177 : : {
1178 : 147 : float4 num = PG_GETARG_FLOAT4(0);
1179 : :
1180 : 147 : PG_RETURN_FLOAT8((float8) num);
1181 : : }
1182 : :
1183 : :
1184 : : /*
1185 : : * dtof - converts a float8 number to a float4 number
1186 : : */
1187 : : Datum
1188 : 18 : dtof(PG_FUNCTION_ARGS)
1189 : : {
1190 : 18 : float8 num = PG_GETARG_FLOAT8(0);
1191 : : float4 result;
1192 : :
1522 1193 : 18 : result = (float4) num;
1194 [ + + + - ]: 18 : if (unlikely(isinf(result)) && !isinf(num))
1195 : 6 : float_overflow_error();
1196 [ + + + - ]: 12 : if (unlikely(result == 0.0f) && num != 0.0)
1197 : 6 : float_underflow_error();
1198 : :
1199 : 6 : PG_RETURN_FLOAT4(result);
1200 : : }
1201 : :
1202 : :
1203 : : /*
1204 : : * dtoi4 - converts a float8 number to an int4 number
1205 : : */
1206 : : Datum
8657 1207 : 316242 : dtoi4(PG_FUNCTION_ARGS)
1208 : : {
1209 : 316242 : float8 num = PG_GETARG_FLOAT8(0);
1210 : :
1211 : : /*
1212 : : * Get rid of any fractional part in the input. This is so we don't fail
1213 : : * on just-out-of-range values that would round into range. Note
1214 : : * assumption that rint() will pass through a NaN or Inf unchanged.
1215 : : */
1969 1216 : 316242 : num = rint(num);
1217 : :
1218 : : /* Range check */
1620 1219 [ + + + + : 316242 : if (unlikely(isnan(num) || !FLOAT8_FITS_IN_INT32(num)))
+ + + + +
+ ]
7567 1220 [ + - ]: 12 : ereport(ERROR,
1221 : : (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
1222 : : errmsg("integer out of range")));
1223 : :
1969 1224 : 316230 : PG_RETURN_INT32((int32) num);
1225 : : }
1226 : :
1227 : :
1228 : : /*
1229 : : * dtoi2 - converts a float8 number to an int2 number
1230 : : */
1231 : : Datum
8714 1232 : 45 : dtoi2(PG_FUNCTION_ARGS)
1233 : : {
1234 : 45 : float8 num = PG_GETARG_FLOAT8(0);
1235 : :
1236 : : /*
1237 : : * Get rid of any fractional part in the input. This is so we don't fail
1238 : : * on just-out-of-range values that would round into range. Note
1239 : : * assumption that rint() will pass through a NaN or Inf unchanged.
1240 : : */
1969 1241 : 45 : num = rint(num);
1242 : :
1243 : : /* Range check */
1620 1244 [ + - + + : 45 : if (unlikely(isnan(num) || !FLOAT8_FITS_IN_INT16(num)))
+ + + + +
+ ]
7567 1245 [ + - ]: 6 : ereport(ERROR,
1246 : : (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
1247 : : errmsg("smallint out of range")));
1248 : :
1969 1249 : 39 : PG_RETURN_INT16((int16) num);
1250 : : }
1251 : :
1252 : :
1253 : : /*
1254 : : * i4tod - converts an int4 number to a float8 number
1255 : : */
1256 : : Datum
8706 1257 : 1159726 : i4tod(PG_FUNCTION_ARGS)
1258 : : {
1259 : 1159726 : int32 num = PG_GETARG_INT32(0);
1260 : :
6312 bruce@momjian.us 1261 : 1159726 : PG_RETURN_FLOAT8((float8) num);
1262 : : }
1263 : :
1264 : :
1265 : : /*
1266 : : * i2tod - converts an int2 number to a float8 number
1267 : : */
1268 : : Datum
8714 tgl@sss.pgh.pa.us 1269 : 123 : i2tod(PG_FUNCTION_ARGS)
1270 : : {
1271 : 123 : int16 num = PG_GETARG_INT16(0);
1272 : :
6312 bruce@momjian.us 1273 : 123 : PG_RETURN_FLOAT8((float8) num);
1274 : : }
1275 : :
1276 : :
1277 : : /*
1278 : : * ftoi4 - converts a float4 number to an int4 number
1279 : : */
1280 : : Datum
8657 tgl@sss.pgh.pa.us 1281 : 12 : ftoi4(PG_FUNCTION_ARGS)
1282 : : {
1283 : 12 : float4 num = PG_GETARG_FLOAT4(0);
1284 : :
1285 : : /*
1286 : : * Get rid of any fractional part in the input. This is so we don't fail
1287 : : * on just-out-of-range values that would round into range. Note
1288 : : * assumption that rint() will pass through a NaN or Inf unchanged.
1289 : : */
1969 1290 : 12 : num = rint(num);
1291 : :
1292 : : /* Range check */
1620 1293 [ + - + + : 12 : if (unlikely(isnan(num) || !FLOAT4_FITS_IN_INT32(num)))
+ + + + +
+ ]
7567 1294 [ + - ]: 6 : ereport(ERROR,
1295 : : (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
1296 : : errmsg("integer out of range")));
1297 : :
1969 1298 : 6 : PG_RETURN_INT32((int32) num);
1299 : : }
1300 : :
1301 : :
1302 : : /*
1303 : : * ftoi2 - converts a float4 number to an int2 number
1304 : : */
1305 : : Datum
8714 1306 : 12 : ftoi2(PG_FUNCTION_ARGS)
1307 : : {
1308 : 12 : float4 num = PG_GETARG_FLOAT4(0);
1309 : :
1310 : : /*
1311 : : * Get rid of any fractional part in the input. This is so we don't fail
1312 : : * on just-out-of-range values that would round into range. Note
1313 : : * assumption that rint() will pass through a NaN or Inf unchanged.
1314 : : */
1969 1315 : 12 : num = rint(num);
1316 : :
1317 : : /* Range check */
1620 1318 [ + - + + : 12 : if (unlikely(isnan(num) || !FLOAT4_FITS_IN_INT16(num)))
+ + + + +
+ ]
7567 1319 [ + - ]: 6 : ereport(ERROR,
1320 : : (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
1321 : : errmsg("smallint out of range")));
1322 : :
1969 1323 : 6 : PG_RETURN_INT16((int16) num);
1324 : : }
1325 : :
1326 : :
1327 : : /*
1328 : : * i4tof - converts an int4 number to a float4 number
1329 : : */
1330 : : Datum
8706 1331 : 232 : i4tof(PG_FUNCTION_ARGS)
1332 : : {
1333 : 232 : int32 num = PG_GETARG_INT32(0);
1334 : :
6312 bruce@momjian.us 1335 : 232 : PG_RETURN_FLOAT4((float4) num);
1336 : : }
1337 : :
1338 : :
1339 : : /*
1340 : : * i2tof - converts an int2 number to a float4 number
1341 : : */
1342 : : Datum
8714 tgl@sss.pgh.pa.us 1343 :UBC 0 : i2tof(PG_FUNCTION_ARGS)
1344 : : {
1345 : 0 : int16 num = PG_GETARG_INT16(0);
1346 : :
6312 bruce@momjian.us 1347 : 0 : PG_RETURN_FLOAT4((float4) num);
1348 : : }
1349 : :
1350 : :
1351 : : /*
1352 : : * =======================
1353 : : * RANDOM FLOAT8 OPERATORS
1354 : : * =======================
1355 : : */
1356 : :
1357 : : /*
1358 : : * dround - returns ROUND(arg1)
1359 : : */
1360 : : Datum
8657 tgl@sss.pgh.pa.us 1361 :CBC 9768 : dround(PG_FUNCTION_ARGS)
1362 : : {
1363 : 9768 : float8 arg1 = PG_GETARG_FLOAT8(0);
1364 : :
6312 bruce@momjian.us 1365 : 9768 : PG_RETURN_FLOAT8(rint(arg1));
1366 : : }
1367 : :
1368 : : /*
1369 : : * dceil - returns the smallest integer greater than or
1370 : : * equal to the specified float
1371 : : */
1372 : : Datum
7848 1373 : 30 : dceil(PG_FUNCTION_ARGS)
1374 : : {
1375 : 30 : float8 arg1 = PG_GETARG_FLOAT8(0);
1376 : :
1377 : 30 : PG_RETURN_FLOAT8(ceil(arg1));
1378 : : }
1379 : :
1380 : : /*
1381 : : * dfloor - returns the largest integer lesser than or
1382 : : * equal to the specified float
1383 : : */
1384 : : Datum
1385 : 30 : dfloor(PG_FUNCTION_ARGS)
1386 : : {
1387 : 30 : float8 arg1 = PG_GETARG_FLOAT8(0);
1388 : :
1389 : 30 : PG_RETURN_FLOAT8(floor(arg1));
1390 : : }
1391 : :
1392 : : /*
1393 : : * dsign - returns -1 if the argument is less than 0, 0
1394 : : * if the argument is equal to 0, and 1 if the
1395 : : * argument is greater than zero.
1396 : : */
1397 : : Datum
1398 : 15 : dsign(PG_FUNCTION_ARGS)
1399 : : {
1400 : 15 : float8 arg1 = PG_GETARG_FLOAT8(0);
1401 : : float8 result;
1402 : :
1403 [ + + ]: 15 : if (arg1 > 0)
1404 : 9 : result = 1.0;
1405 [ + + ]: 6 : else if (arg1 < 0)
1406 : 3 : result = -1.0;
1407 : : else
1408 : 3 : result = 0.0;
1409 : :
1410 : 15 : PG_RETURN_FLOAT8(result);
1411 : : }
1412 : :
1413 : : /*
1414 : : * dtrunc - returns truncation-towards-zero of arg1,
1415 : : * arg1 >= 0 ... the greatest integer less
1416 : : * than or equal to arg1
1417 : : * arg1 < 0 ... the least integer greater
1418 : : * than or equal to arg1
1419 : : */
1420 : : Datum
8657 tgl@sss.pgh.pa.us 1421 : 18 : dtrunc(PG_FUNCTION_ARGS)
1422 : : {
1423 : 18 : float8 arg1 = PG_GETARG_FLOAT8(0);
1424 : : float8 result;
1425 : :
1426 [ + + ]: 18 : if (arg1 >= 0)
1427 : 15 : result = floor(arg1);
1428 : : else
1429 : 3 : result = -floor(-arg1);
1430 : :
1431 : 18 : PG_RETURN_FLOAT8(result);
1432 : : }
1433 : :
1434 : :
1435 : : /*
1436 : : * dsqrt - returns square root of arg1
1437 : : */
1438 : : Datum
1439 : 2033 : dsqrt(PG_FUNCTION_ARGS)
1440 : : {
1441 : 2033 : float8 arg1 = PG_GETARG_FLOAT8(0);
1442 : : float8 result;
1443 : :
1444 [ - + ]: 2033 : if (arg1 < 0)
7567 tgl@sss.pgh.pa.us 1445 [ # # ]:UBC 0 : ereport(ERROR,
1446 : : (errcode(ERRCODE_INVALID_ARGUMENT_FOR_POWER_FUNCTION),
1447 : : errmsg("cannot take square root of a negative number")));
1448 : :
8657 tgl@sss.pgh.pa.us 1449 :CBC 2033 : result = sqrt(arg1);
1522 1450 [ - + - - ]: 2033 : if (unlikely(isinf(result)) && !isinf(arg1))
1522 tgl@sss.pgh.pa.us 1451 :UBC 0 : float_overflow_error();
1522 tgl@sss.pgh.pa.us 1452 [ + + - + ]:CBC 2033 : if (unlikely(result == 0.0) && arg1 != 0.0)
1522 tgl@sss.pgh.pa.us 1453 :UBC 0 : float_underflow_error();
1454 : :
8657 tgl@sss.pgh.pa.us 1455 :CBC 2033 : PG_RETURN_FLOAT8(result);
1456 : : }
1457 : :
1458 : :
1459 : : /*
1460 : : * dcbrt - returns cube root of arg1
1461 : : */
1462 : : Datum
1463 : 18 : dcbrt(PG_FUNCTION_ARGS)
1464 : : {
1465 : 18 : float8 arg1 = PG_GETARG_FLOAT8(0);
1466 : : float8 result;
1467 : :
1468 : 18 : result = cbrt(arg1);
1522 1469 [ - + - - ]: 18 : if (unlikely(isinf(result)) && !isinf(arg1))
1522 tgl@sss.pgh.pa.us 1470 :UBC 0 : float_overflow_error();
1522 tgl@sss.pgh.pa.us 1471 [ + + - + ]:CBC 18 : if (unlikely(result == 0.0) && arg1 != 0.0)
1522 tgl@sss.pgh.pa.us 1472 :UBC 0 : float_underflow_error();
1473 : :
8657 tgl@sss.pgh.pa.us 1474 :CBC 18 : PG_RETURN_FLOAT8(result);
1475 : : }
1476 : :
1477 : :
1478 : : /*
1479 : : * dpow - returns pow(arg1,arg2)
1480 : : */
1481 : : Datum
1482 : 337 : dpow(PG_FUNCTION_ARGS)
1483 : : {
1484 : 337 : float8 arg1 = PG_GETARG_FLOAT8(0);
1485 : 337 : float8 arg2 = PG_GETARG_FLOAT8(1);
1486 : : float8 result;
1487 : :
1488 : : /*
1489 : : * The POSIX spec says that NaN ^ 0 = 1, and 1 ^ NaN = 1, while all other
1490 : : * cases with NaN inputs yield NaN (with no error). Many older platforms
1491 : : * get one or more of these cases wrong, so deal with them via explicit
1492 : : * logic rather than trusting pow(3).
1493 : : */
2177 1494 [ + + ]: 337 : if (isnan(arg1))
1495 : : {
1496 [ + + + + ]: 9 : if (isnan(arg2) || arg2 != 0.0)
1497 : 6 : PG_RETURN_FLOAT8(get_float8_nan());
1498 : 3 : PG_RETURN_FLOAT8(1.0);
1499 : : }
1500 [ + + ]: 328 : if (isnan(arg2))
1501 : : {
1502 [ + + ]: 9 : if (arg1 != 1.0)
1503 : 6 : PG_RETURN_FLOAT8(get_float8_nan());
1504 : 3 : PG_RETURN_FLOAT8(1.0);
1505 : : }
1506 : :
1507 : : /*
1508 : : * The SQL spec requires that we emit a particular SQLSTATE error code for
1509 : : * certain error conditions. Specifically, we don't return a
1510 : : * divide-by-zero error code for 0 ^ -1.
1511 : : */
5819 bruce@momjian.us 1512 [ + + + + ]: 319 : if (arg1 == 0 && arg2 < 0)
7273 neilc@samurai.com 1513 [ + - ]: 3 : ereport(ERROR,
1514 : : (errcode(ERRCODE_INVALID_ARGUMENT_FOR_POWER_FUNCTION),
1515 : : errmsg("zero raised to a negative power is undefined")));
5819 bruce@momjian.us 1516 [ + + + + ]: 316 : if (arg1 < 0 && floor(arg2) != arg2)
1517 [ + - ]: 3 : ereport(ERROR,
1518 : : (errcode(ERRCODE_INVALID_ARGUMENT_FOR_POWER_FUNCTION),
1519 : : errmsg("a negative number raised to a non-integer power yields a complex result")));
1520 : :
1521 : : /*
1522 : : * We don't trust the platform's pow() to handle infinity cases per POSIX
1523 : : * spec either, so deal with those explicitly too. It's easier to handle
1524 : : * infinite y first, so that it doesn't matter if x is also infinite.
1525 : : */
1399 tgl@sss.pgh.pa.us 1526 [ + + ]: 313 : if (isinf(arg2))
1527 : : {
1398 1528 : 51 : float8 absx = fabs(arg1);
1529 : :
1399 1530 [ + + ]: 51 : if (absx == 1.0)
1531 : 12 : result = 1.0;
1532 [ + + ]: 39 : else if (arg2 > 0.0) /* y = +Inf */
1533 : : {
1534 [ + + ]: 21 : if (absx > 1.0)
1535 : 12 : result = arg2;
1536 : : else
1537 : 9 : result = 0.0;
1538 : : }
1539 : : else /* y = -Inf */
1540 : : {
1541 [ + + ]: 18 : if (absx > 1.0)
1542 : 12 : result = 0.0;
1543 : : else
1544 : 6 : result = -arg2;
1545 : : }
1546 : : }
1547 [ + + ]: 262 : else if (isinf(arg1))
1548 : : {
1549 [ + + ]: 24 : if (arg2 == 0.0)
1550 : 6 : result = 1.0;
1551 [ + + ]: 18 : else if (arg1 > 0.0) /* x = +Inf */
1552 : : {
1553 [ + + ]: 6 : if (arg2 > 0.0)
1554 : 3 : result = arg1;
1555 : : else
1556 : 3 : result = 0.0;
1557 : : }
1558 : : else /* x = -Inf */
1559 : : {
1560 : : /*
1561 : : * Per POSIX, the sign of the result depends on whether y is an
1562 : : * odd integer. Since x < 0, we already know from the previous
1563 : : * domain check that y is an integer. It is odd if y/2 is not
1564 : : * also an integer.
1565 : : */
1398 1566 : 12 : float8 halfy = arg2 / 2; /* should be computed exactly */
1567 : 12 : bool yisoddinteger = (floor(halfy) != halfy);
1568 : :
1399 1569 [ + + ]: 12 : if (arg2 > 0.0)
1570 [ + + ]: 6 : result = yisoddinteger ? arg1 : -arg1;
1571 : : else
1572 [ + + ]: 6 : result = yisoddinteger ? -0.0 : 0.0;
1573 : : }
1574 : : }
1575 : : else
1576 : : {
1577 : : /*
1578 : : * pow() sets errno on only some platforms, depending on whether it
1579 : : * follows _IEEE_, _POSIX_, _XOPEN_, or _SVID_, so we must check both
1580 : : * errno and invalid output values. (We can't rely on just the
1581 : : * latter, either; some old platforms return a large-but-finite
1582 : : * HUGE_VAL when reporting overflow.)
1583 : : */
1584 : 238 : errno = 0;
1585 : 238 : result = pow(arg1, arg2);
1586 [ + - - + ]: 238 : if (errno == EDOM || isnan(result))
1587 : : {
1588 : : /*
1589 : : * We handled all possible domain errors above, so this should be
1590 : : * impossible. However, old glibc versions on x86 have a bug that
1591 : : * causes them to fail this way for abs(y) greater than 2^63:
1592 : : *
1593 : : * https://sourceware.org/bugzilla/show_bug.cgi?id=3866
1594 : : *
1595 : : * Hence, if we get here, assume y is finite but large (large
1596 : : * enough to be certainly even). The result should be 0 if x == 0,
1597 : : * 1.0 if abs(x) == 1.0, otherwise an overflow or underflow error.
1598 : : */
1399 tgl@sss.pgh.pa.us 1599 [ # # ]:UBC 0 : if (arg1 == 0.0)
1600 : 0 : result = 0.0; /* we already verified y is positive */
1601 : : else
1602 : : {
1398 1603 : 0 : float8 absx = fabs(arg1);
1604 : :
1399 1605 [ # # ]: 0 : if (absx == 1.0)
1606 : 0 : result = 1.0;
1607 [ # # # # ]: 0 : else if (arg2 >= 0.0 ? (absx > 1.0) : (absx < 1.0))
1608 : 0 : float_overflow_error();
1609 : : else
1610 : 0 : float_underflow_error();
1611 : : }
1612 : : }
1399 tgl@sss.pgh.pa.us 1613 [ + + ]:CBC 238 : else if (errno == ERANGE)
1614 : : {
1615 [ + - ]: 3 : if (result != 0.0)
1616 : 3 : float_overflow_error();
1617 : : else
1399 tgl@sss.pgh.pa.us 1618 :UBC 0 : float_underflow_error();
1619 : : }
1620 : : else
1621 : : {
1399 tgl@sss.pgh.pa.us 1622 [ - + ]:CBC 235 : if (unlikely(isinf(result)))
1399 tgl@sss.pgh.pa.us 1623 :UBC 0 : float_overflow_error();
1399 tgl@sss.pgh.pa.us 1624 [ + + - + ]:CBC 235 : if (unlikely(result == 0.0) && arg1 != 0.0)
1399 tgl@sss.pgh.pa.us 1625 :UBC 0 : float_underflow_error();
1626 : : }
1627 : : }
1628 : :
8657 tgl@sss.pgh.pa.us 1629 :CBC 310 : PG_RETURN_FLOAT8(result);
1630 : : }
1631 : :
1632 : :
1633 : : /*
1634 : : * dexp - returns the exponential function of arg1
1635 : : */
1636 : : Datum
1637 : 27 : dexp(PG_FUNCTION_ARGS)
1638 : : {
1639 : 27 : float8 arg1 = PG_GETARG_FLOAT8(0);
1640 : : float8 result;
1641 : :
1642 : : /*
1643 : : * Handle NaN and Inf cases explicitly. This avoids needing to assume
1644 : : * that the platform's exp() conforms to POSIX for these cases, and it
1645 : : * removes some edge cases for the overflow checks below.
1646 : : */
1400 1647 [ + + ]: 27 : if (isnan(arg1))
1648 : 3 : result = arg1;
1649 [ + + ]: 24 : else if (isinf(arg1))
1650 : : {
1651 : : /* Per POSIX, exp(-Inf) is 0 */
1652 [ + + ]: 6 : result = (arg1 > 0.0) ? arg1 : 0;
1653 : : }
1654 : : else
1655 : : {
1656 : : /*
1657 : : * On some platforms, exp() will not set errno but just return Inf or
1658 : : * zero to report overflow/underflow; therefore, test both cases.
1659 : : */
1660 : 18 : errno = 0;
1661 : 18 : result = exp(arg1);
1662 [ + + ]: 18 : if (unlikely(errno == ERANGE))
1663 : : {
1664 [ - + ]: 3 : if (result != 0.0)
1400 tgl@sss.pgh.pa.us 1665 :UBC 0 : float_overflow_error();
1666 : : else
1400 tgl@sss.pgh.pa.us 1667 :CBC 3 : float_underflow_error();
1668 : : }
1669 [ - + ]: 15 : else if (unlikely(isinf(result)))
1400 tgl@sss.pgh.pa.us 1670 :UBC 0 : float_overflow_error();
1400 tgl@sss.pgh.pa.us 1671 [ - + ]:CBC 15 : else if (unlikely(result == 0.0))
1400 tgl@sss.pgh.pa.us 1672 :UBC 0 : float_underflow_error();
1673 : : }
1674 : :
8657 tgl@sss.pgh.pa.us 1675 :CBC 24 : PG_RETURN_FLOAT8(result);
1676 : : }
1677 : :
1678 : :
1679 : : /*
1680 : : * dlog1 - returns the natural logarithm of arg1
1681 : : */
1682 : : Datum
1683 : 15 : dlog1(PG_FUNCTION_ARGS)
1684 : : {
1685 : 15 : float8 arg1 = PG_GETARG_FLOAT8(0);
1686 : : float8 result;
1687 : :
1688 : : /*
1689 : : * Emit particular SQLSTATE error codes for ln(). This is required by the
1690 : : * SQL standard.
1691 : : */
1692 [ + + ]: 15 : if (arg1 == 0.0)
7567 1693 [ + - ]: 3 : ereport(ERROR,
1694 : : (errcode(ERRCODE_INVALID_ARGUMENT_FOR_LOG),
1695 : : errmsg("cannot take logarithm of zero")));
8657 1696 [ + + ]: 12 : if (arg1 < 0)
7567 1697 [ + - ]: 3 : ereport(ERROR,
1698 : : (errcode(ERRCODE_INVALID_ARGUMENT_FOR_LOG),
1699 : : errmsg("cannot take logarithm of a negative number")));
1700 : :
8657 1701 : 9 : result = log(arg1);
1522 1702 [ - + - - ]: 9 : if (unlikely(isinf(result)) && !isinf(arg1))
1522 tgl@sss.pgh.pa.us 1703 :UBC 0 : float_overflow_error();
1522 tgl@sss.pgh.pa.us 1704 [ - + - - ]:CBC 9 : if (unlikely(result == 0.0) && arg1 != 1.0)
1522 tgl@sss.pgh.pa.us 1705 :UBC 0 : float_underflow_error();
1706 : :
8657 tgl@sss.pgh.pa.us 1707 :CBC 9 : PG_RETURN_FLOAT8(result);
1708 : : }
1709 : :
1710 : :
1711 : : /*
1712 : : * dlog10 - returns the base 10 logarithm of arg1
1713 : : */
1714 : : Datum
8657 tgl@sss.pgh.pa.us 1715 :UBC 0 : dlog10(PG_FUNCTION_ARGS)
1716 : : {
1717 : 0 : float8 arg1 = PG_GETARG_FLOAT8(0);
1718 : : float8 result;
1719 : :
1720 : : /*
1721 : : * Emit particular SQLSTATE error codes for log(). The SQL spec doesn't
1722 : : * define log(), but it does define ln(), so it makes sense to emit the
1723 : : * same error code for an analogous error condition.
1724 : : */
1725 [ # # ]: 0 : if (arg1 == 0.0)
7567 1726 [ # # ]: 0 : ereport(ERROR,
1727 : : (errcode(ERRCODE_INVALID_ARGUMENT_FOR_LOG),
1728 : : errmsg("cannot take logarithm of zero")));
8657 1729 [ # # ]: 0 : if (arg1 < 0)
7567 1730 [ # # ]: 0 : ereport(ERROR,
1731 : : (errcode(ERRCODE_INVALID_ARGUMENT_FOR_LOG),
1732 : : errmsg("cannot take logarithm of a negative number")));
1733 : :
8657 1734 : 0 : result = log10(arg1);
1522 1735 [ # # # # ]: 0 : if (unlikely(isinf(result)) && !isinf(arg1))
1736 : 0 : float_overflow_error();
1737 [ # # # # ]: 0 : if (unlikely(result == 0.0) && arg1 != 1.0)
1738 : 0 : float_underflow_error();
1739 : :
8657 1740 : 0 : PG_RETURN_FLOAT8(result);
1741 : : }
1742 : :
1743 : :
1744 : : /*
1745 : : * dacos - returns the arccos of arg1 (radians)
1746 : : */
1747 : : Datum
1748 : 0 : dacos(PG_FUNCTION_ARGS)
1749 : : {
1750 : 0 : float8 arg1 = PG_GETARG_FLOAT8(0);
1751 : : float8 result;
1752 : :
1753 : : /* Per the POSIX spec, return NaN if the input is NaN */
3005 1754 [ # # ]: 0 : if (isnan(arg1))
1755 : 0 : PG_RETURN_FLOAT8(get_float8_nan());
1756 : :
1757 : : /*
1758 : : * The principal branch of the inverse cosine function maps values in the
1759 : : * range [-1, 1] to values in the range [0, Pi], so we should reject any
1760 : : * inputs outside that range and the result will always be finite.
1761 : : */
1762 [ # # # # ]: 0 : if (arg1 < -1.0 || arg1 > 1.0)
7567 1763 [ # # ]: 0 : ereport(ERROR,
1764 : : (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
1765 : : errmsg("input is out of range")));
1766 : :
3005 1767 : 0 : result = acos(arg1);
1522 1768 [ # # ]: 0 : if (unlikely(isinf(result)))
1769 : 0 : float_overflow_error();
1770 : :
8657 1771 : 0 : PG_RETURN_FLOAT8(result);
1772 : : }
1773 : :
1774 : :
1775 : : /*
1776 : : * dasin - returns the arcsin of arg1 (radians)
1777 : : */
1778 : : Datum
8657 tgl@sss.pgh.pa.us 1779 :CBC 55 : dasin(PG_FUNCTION_ARGS)
1780 : : {
1781 : 55 : float8 arg1 = PG_GETARG_FLOAT8(0);
1782 : : float8 result;
1783 : :
1784 : : /* Per the POSIX spec, return NaN if the input is NaN */
3005 1785 [ - + ]: 55 : if (isnan(arg1))
3005 tgl@sss.pgh.pa.us 1786 :UBC 0 : PG_RETURN_FLOAT8(get_float8_nan());
1787 : :
1788 : : /*
1789 : : * The principal branch of the inverse sine function maps values in the
1790 : : * range [-1, 1] to values in the range [-Pi/2, Pi/2], so we should reject
1791 : : * any inputs outside that range and the result will always be finite.
1792 : : */
3005 tgl@sss.pgh.pa.us 1793 [ + - - + ]:CBC 55 : if (arg1 < -1.0 || arg1 > 1.0)
7567 tgl@sss.pgh.pa.us 1794 [ # # ]:UBC 0 : ereport(ERROR,
1795 : : (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
1796 : : errmsg("input is out of range")));
1797 : :
3005 tgl@sss.pgh.pa.us 1798 :CBC 55 : result = asin(arg1);
1522 1799 [ - + ]: 55 : if (unlikely(isinf(result)))
1522 tgl@sss.pgh.pa.us 1800 :UBC 0 : float_overflow_error();
1801 : :
8657 tgl@sss.pgh.pa.us 1802 :CBC 55 : PG_RETURN_FLOAT8(result);
1803 : : }
1804 : :
1805 : :
1806 : : /*
1807 : : * datan - returns the arctan of arg1 (radians)
1808 : : */
1809 : : Datum
8657 tgl@sss.pgh.pa.us 1810 :UBC 0 : datan(PG_FUNCTION_ARGS)
1811 : : {
1812 : 0 : float8 arg1 = PG_GETARG_FLOAT8(0);
1813 : : float8 result;
1814 : :
1815 : : /* Per the POSIX spec, return NaN if the input is NaN */
3005 1816 [ # # ]: 0 : if (isnan(arg1))
1817 : 0 : PG_RETURN_FLOAT8(get_float8_nan());
1818 : :
1819 : : /*
1820 : : * The principal branch of the inverse tangent function maps all inputs to
1821 : : * values in the range [-Pi/2, Pi/2], so the result should always be
1822 : : * finite, even if the input is infinite.
1823 : : */
8657 1824 : 0 : result = atan(arg1);
1522 1825 [ # # ]: 0 : if (unlikely(isinf(result)))
1826 : 0 : float_overflow_error();
1827 : :
8657 1828 : 0 : PG_RETURN_FLOAT8(result);
1829 : : }
1830 : :
1831 : :
1832 : : /*
1833 : : * atan2 - returns the arctan of arg1/arg2 (radians)
1834 : : */
1835 : : Datum
8657 tgl@sss.pgh.pa.us 1836 :CBC 20 : datan2(PG_FUNCTION_ARGS)
1837 : : {
1838 : 20 : float8 arg1 = PG_GETARG_FLOAT8(0);
1839 : 20 : float8 arg2 = PG_GETARG_FLOAT8(1);
1840 : : float8 result;
1841 : :
1842 : : /* Per the POSIX spec, return NaN if either input is NaN */
3005 1843 [ + - - + ]: 20 : if (isnan(arg1) || isnan(arg2))
3005 tgl@sss.pgh.pa.us 1844 :UBC 0 : PG_RETURN_FLOAT8(get_float8_nan());
1845 : :
1846 : : /*
1847 : : * atan2 maps all inputs to values in the range [-Pi, Pi], so the result
1848 : : * should always be finite, even if the inputs are infinite.
1849 : : */
8657 tgl@sss.pgh.pa.us 1850 :CBC 20 : result = atan2(arg1, arg2);
1522 1851 [ - + ]: 20 : if (unlikely(isinf(result)))
1522 tgl@sss.pgh.pa.us 1852 :UBC 0 : float_overflow_error();
1853 : :
8657 tgl@sss.pgh.pa.us 1854 :CBC 20 : PG_RETURN_FLOAT8(result);
1855 : : }
1856 : :
1857 : :
1858 : : /*
1859 : : * dcos - returns the cosine of arg1 (radians)
1860 : : */
1861 : : Datum
1862 : 585 : dcos(PG_FUNCTION_ARGS)
1863 : : {
1864 : 585 : float8 arg1 = PG_GETARG_FLOAT8(0);
1865 : : float8 result;
1866 : :
1867 : : /* Per the POSIX spec, return NaN if the input is NaN */
3005 1868 [ - + ]: 585 : if (isnan(arg1))
3005 tgl@sss.pgh.pa.us 1869 :UBC 0 : PG_RETURN_FLOAT8(get_float8_nan());
1870 : :
1871 : : /*
1872 : : * cos() is periodic and so theoretically can work for all finite inputs,
1873 : : * but some implementations may choose to throw error if the input is so
1874 : : * large that there are no significant digits in the result. So we should
1875 : : * check for errors. POSIX allows an error to be reported either via
1876 : : * errno or via fetestexcept(), but currently we only support checking
1877 : : * errno. (fetestexcept() is rumored to report underflow unreasonably
1878 : : * early on some platforms, so it's not clear that believing it would be a
1879 : : * net improvement anyway.)
1880 : : *
1881 : : * For infinite inputs, POSIX specifies that the trigonometric functions
1882 : : * should return a domain error; but we won't notice that unless the
1883 : : * platform reports via errno, so also explicitly test for infinite
1884 : : * inputs.
1885 : : */
6708 bruce@momjian.us 1886 :CBC 585 : errno = 0;
8657 tgl@sss.pgh.pa.us 1887 : 585 : result = cos(arg1);
3005 1888 [ + - - + ]: 585 : if (errno != 0 || isinf(arg1))
7567 tgl@sss.pgh.pa.us 1889 [ # # ]:UBC 0 : ereport(ERROR,
1890 : : (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
1891 : : errmsg("input is out of range")));
1522 tgl@sss.pgh.pa.us 1892 [ - + ]:CBC 585 : if (unlikely(isinf(result)))
1522 tgl@sss.pgh.pa.us 1893 :UBC 0 : float_overflow_error();
1894 : :
8657 tgl@sss.pgh.pa.us 1895 :CBC 585 : PG_RETURN_FLOAT8(result);
1896 : : }
1897 : :
1898 : :
1899 : : /*
1900 : : * dcot - returns the cotangent of arg1 (radians)
1901 : : */
1902 : : Datum
8657 tgl@sss.pgh.pa.us 1903 :UBC 0 : dcot(PG_FUNCTION_ARGS)
1904 : : {
1905 : 0 : float8 arg1 = PG_GETARG_FLOAT8(0);
1906 : : float8 result;
1907 : :
1908 : : /* Per the POSIX spec, return NaN if the input is NaN */
3005 1909 [ # # ]: 0 : if (isnan(arg1))
1910 : 0 : PG_RETURN_FLOAT8(get_float8_nan());
1911 : :
1912 : : /* Be sure to throw an error if the input is infinite --- see dcos() */
6708 bruce@momjian.us 1913 : 0 : errno = 0;
8657 tgl@sss.pgh.pa.us 1914 : 0 : result = tan(arg1);
3005 1915 [ # # # # ]: 0 : if (errno != 0 || isinf(arg1))
7567 1916 [ # # ]: 0 : ereport(ERROR,
1917 : : (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
1918 : : errmsg("input is out of range")));
1919 : :
8657 1920 : 0 : result = 1.0 / result;
1921 : : /* Not checking for overflow because cot(0) == Inf */
1922 : :
1923 : 0 : PG_RETURN_FLOAT8(result);
1924 : : }
1925 : :
1926 : :
1927 : : /*
1928 : : * dsin - returns the sine of arg1 (radians)
1929 : : */
1930 : : Datum
8657 tgl@sss.pgh.pa.us 1931 :CBC 469 : dsin(PG_FUNCTION_ARGS)
1932 : : {
1933 : 469 : float8 arg1 = PG_GETARG_FLOAT8(0);
1934 : : float8 result;
1935 : :
1936 : : /* Per the POSIX spec, return NaN if the input is NaN */
3005 1937 [ - + ]: 469 : if (isnan(arg1))
3005 tgl@sss.pgh.pa.us 1938 :UBC 0 : PG_RETURN_FLOAT8(get_float8_nan());
1939 : :
1940 : : /* Be sure to throw an error if the input is infinite --- see dcos() */
6708 bruce@momjian.us 1941 :CBC 469 : errno = 0;
8657 tgl@sss.pgh.pa.us 1942 : 469 : result = sin(arg1);
3005 1943 [ + - - + ]: 469 : if (errno != 0 || isinf(arg1))
7567 tgl@sss.pgh.pa.us 1944 [ # # ]:UBC 0 : ereport(ERROR,
1945 : : (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
1946 : : errmsg("input is out of range")));
1522 tgl@sss.pgh.pa.us 1947 [ - + ]:CBC 469 : if (unlikely(isinf(result)))
1522 tgl@sss.pgh.pa.us 1948 :UBC 0 : float_overflow_error();
1949 : :
8657 tgl@sss.pgh.pa.us 1950 :CBC 469 : PG_RETURN_FLOAT8(result);
1951 : : }
1952 : :
1953 : :
1954 : : /*
1955 : : * dtan - returns the tangent of arg1 (radians)
1956 : : */
1957 : : Datum
8657 tgl@sss.pgh.pa.us 1958 :UBC 0 : dtan(PG_FUNCTION_ARGS)
1959 : : {
1960 : 0 : float8 arg1 = PG_GETARG_FLOAT8(0);
1961 : : float8 result;
1962 : :
1963 : : /* Per the POSIX spec, return NaN if the input is NaN */
3005 1964 [ # # ]: 0 : if (isnan(arg1))
1965 : 0 : PG_RETURN_FLOAT8(get_float8_nan());
1966 : :
1967 : : /* Be sure to throw an error if the input is infinite --- see dcos() */
6708 bruce@momjian.us 1968 : 0 : errno = 0;
8657 tgl@sss.pgh.pa.us 1969 : 0 : result = tan(arg1);
3005 1970 [ # # # # ]: 0 : if (errno != 0 || isinf(arg1))
7567 1971 [ # # ]: 0 : ereport(ERROR,
1972 : : (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
1973 : : errmsg("input is out of range")));
1974 : : /* Not checking for overflow because tan(pi/2) == Inf */
1975 : :
8657 1976 : 0 : PG_RETURN_FLOAT8(result);
1977 : : }
1978 : :
1979 : :
1980 : : /* ========== DEGREE-BASED TRIGONOMETRIC FUNCTIONS ========== */
1981 : :
1982 : :
1983 : : /*
1984 : : * Initialize the cached constants declared at the head of this file
1985 : : * (sin_30 etc). The fact that we need those at all, let alone need this
1986 : : * Rube-Goldberg-worthy method of initializing them, is because there are
1987 : : * compilers out there that will precompute expressions such as sin(constant)
1988 : : * using a sin() function different from what will be used at runtime. If we
1989 : : * want exact results, we must ensure that none of the scaling constants used
1990 : : * in the degree-based trig functions are computed that way. To do so, we
1991 : : * compute them from the variables degree_c_thirty etc, which are also really
1992 : : * constants, but the compiler cannot assume that.
1993 : : *
1994 : : * Other hazards we are trying to forestall with this kluge include the
1995 : : * possibility that compilers will rearrange the expressions, or compute
1996 : : * some intermediate results in registers wider than a standard double.
1997 : : *
1998 : : * In the places where we use these constants, the typical pattern is like
1999 : : * volatile float8 sin_x = sin(x * RADIANS_PER_DEGREE);
2000 : : * return (sin_x / sin_30);
2001 : : * where we hope to get a value of exactly 1.0 from the division when x = 30.
2002 : : * The volatile temporary variable is needed on machines with wide float
2003 : : * registers, to ensure that the result of sin(x) is rounded to double width
2004 : : * the same as the value of sin_30 has been. Experimentation with gcc shows
2005 : : * that marking the temp variable volatile is necessary to make the store and
2006 : : * reload actually happen; hopefully the same trick works for other compilers.
2007 : : * (gcc's documentation suggests using the -ffloat-store compiler switch to
2008 : : * ensure this, but that is compiler-specific and it also pessimizes code in
2009 : : * many places where we don't care about this.)
2010 : : */
2011 : : static void
2911 tgl@sss.pgh.pa.us 2012 :CBC 3 : init_degree_constants(void)
2013 : : {
2014 : 3 : sin_30 = sin(degree_c_thirty * RADIANS_PER_DEGREE);
2015 : 3 : one_minus_cos_60 = 1.0 - cos(degree_c_sixty * RADIANS_PER_DEGREE);
2016 : 3 : asin_0_5 = asin(degree_c_one_half);
2017 : 3 : acos_0_5 = acos(degree_c_one_half);
2018 : 3 : atan_1_0 = atan(degree_c_one);
2019 : 3 : tan_45 = sind_q1(degree_c_forty_five) / cosd_q1(degree_c_forty_five);
2020 : 3 : cot_45 = cosd_q1(degree_c_forty_five) / sind_q1(degree_c_forty_five);
3004 2021 : 3 : degree_consts_set = true;
2022 : 3 : }
2023 : :
2024 : : #define INIT_DEGREE_CONSTANTS() \
2025 : : do { \
2026 : : if (!degree_consts_set) \
2027 : : init_degree_constants(); \
2028 : : } while(0)
2029 : :
2030 : :
2031 : : /*
2032 : : * asind_q1 - returns the inverse sine of x in degrees, for x in
2033 : : * the range [0, 1]. The result is an angle in the
2034 : : * first quadrant --- [0, 90] degrees.
2035 : : *
2036 : : * For the 3 special case inputs (0, 0.5 and 1), this
2037 : : * function will return exact values (0, 30 and 90
2038 : : * degrees respectively).
2039 : : */
2040 : : static double
3005 2041 : 42 : asind_q1(double x)
2042 : : {
2043 : : /*
2044 : : * Stitch together inverse sine and cosine functions for the ranges [0,
2045 : : * 0.5] and (0.5, 1]. Each expression below is guaranteed to return
2046 : : * exactly 30 for x=0.5, so the result is a continuous monotonic function
2047 : : * over the full range.
2048 : : */
2049 [ + + ]: 42 : if (x <= 0.5)
2050 : : {
2910 2051 : 24 : volatile float8 asin_x = asin(x);
2052 : :
2053 : 24 : return (asin_x / asin_0_5) * 30.0;
2054 : : }
2055 : : else
2056 : : {
2057 : 18 : volatile float8 acos_x = acos(x);
2058 : :
2059 : 18 : return 90.0 - (acos_x / acos_0_5) * 60.0;
2060 : : }
2061 : : }
2062 : :
2063 : :
2064 : : /*
2065 : : * acosd_q1 - returns the inverse cosine of x in degrees, for x in
2066 : : * the range [0, 1]. The result is an angle in the
2067 : : * first quadrant --- [0, 90] degrees.
2068 : : *
2069 : : * For the 3 special case inputs (0, 0.5 and 1), this
2070 : : * function will return exact values (0, 60 and 90
2071 : : * degrees respectively).
2072 : : */
2073 : : static double
3005 2074 : 18 : acosd_q1(double x)
2075 : : {
2076 : : /*
2077 : : * Stitch together inverse sine and cosine functions for the ranges [0,
2078 : : * 0.5] and (0.5, 1]. Each expression below is guaranteed to return
2079 : : * exactly 60 for x=0.5, so the result is a continuous monotonic function
2080 : : * over the full range.
2081 : : */
2082 [ + + ]: 18 : if (x <= 0.5)
2083 : : {
2910 2084 : 12 : volatile float8 asin_x = asin(x);
2085 : :
2086 : 12 : return 90.0 - (asin_x / asin_0_5) * 30.0;
2087 : : }
2088 : : else
2089 : : {
2090 : 6 : volatile float8 acos_x = acos(x);
2091 : :
2092 : 6 : return (acos_x / acos_0_5) * 60.0;
2093 : : }
2094 : : }
2095 : :
2096 : :
2097 : : /*
2098 : : * dacosd - returns the arccos of arg1 (degrees)
2099 : : */
2100 : : Datum
3005 2101 : 30 : dacosd(PG_FUNCTION_ARGS)
2102 : : {
2103 : 30 : float8 arg1 = PG_GETARG_FLOAT8(0);
2104 : : float8 result;
2105 : :
2106 : : /* Per the POSIX spec, return NaN if the input is NaN */
2107 [ - + ]: 30 : if (isnan(arg1))
3005 tgl@sss.pgh.pa.us 2108 :UBC 0 : PG_RETURN_FLOAT8(get_float8_nan());
2109 : :
3004 tgl@sss.pgh.pa.us 2110 [ - + ]:CBC 30 : INIT_DEGREE_CONSTANTS();
2111 : :
2112 : : /*
2113 : : * The principal branch of the inverse cosine function maps values in the
2114 : : * range [-1, 1] to values in the range [0, 180], so we should reject any
2115 : : * inputs outside that range and the result will always be finite.
2116 : : */
3005 2117 [ + - - + ]: 30 : if (arg1 < -1.0 || arg1 > 1.0)
3005 tgl@sss.pgh.pa.us 2118 [ # # ]:UBC 0 : ereport(ERROR,
2119 : : (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
2120 : : errmsg("input is out of range")));
2121 : :
3005 tgl@sss.pgh.pa.us 2122 [ + + ]:CBC 30 : if (arg1 >= 0.0)
2123 : 18 : result = acosd_q1(arg1);
2124 : : else
2125 : 12 : result = 90.0 + asind_q1(-arg1);
2126 : :
1522 2127 [ - + ]: 30 : if (unlikely(isinf(result)))
1522 tgl@sss.pgh.pa.us 2128 :UBC 0 : float_overflow_error();
2129 : :
3005 tgl@sss.pgh.pa.us 2130 :CBC 30 : PG_RETURN_FLOAT8(result);
2131 : : }
2132 : :
2133 : :
2134 : : /*
2135 : : * dasind - returns the arcsin of arg1 (degrees)
2136 : : */
2137 : : Datum
2138 : 30 : dasind(PG_FUNCTION_ARGS)
2139 : : {
2140 : 30 : float8 arg1 = PG_GETARG_FLOAT8(0);
2141 : : float8 result;
2142 : :
2143 : : /* Per the POSIX spec, return NaN if the input is NaN */
2144 [ - + ]: 30 : if (isnan(arg1))
3005 tgl@sss.pgh.pa.us 2145 :UBC 0 : PG_RETURN_FLOAT8(get_float8_nan());
2146 : :
3004 tgl@sss.pgh.pa.us 2147 [ - + ]:CBC 30 : INIT_DEGREE_CONSTANTS();
2148 : :
2149 : : /*
2150 : : * The principal branch of the inverse sine function maps values in the
2151 : : * range [-1, 1] to values in the range [-90, 90], so we should reject any
2152 : : * inputs outside that range and the result will always be finite.
2153 : : */
3005 2154 [ + - - + ]: 30 : if (arg1 < -1.0 || arg1 > 1.0)
3005 tgl@sss.pgh.pa.us 2155 [ # # ]:UBC 0 : ereport(ERROR,
2156 : : (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
2157 : : errmsg("input is out of range")));
2158 : :
3005 tgl@sss.pgh.pa.us 2159 [ + + ]:CBC 30 : if (arg1 >= 0.0)
2160 : 18 : result = asind_q1(arg1);
2161 : : else
2162 : 12 : result = -asind_q1(-arg1);
2163 : :
1522 2164 [ - + ]: 30 : if (unlikely(isinf(result)))
1522 tgl@sss.pgh.pa.us 2165 :UBC 0 : float_overflow_error();
2166 : :
3005 tgl@sss.pgh.pa.us 2167 :CBC 30 : PG_RETURN_FLOAT8(result);
2168 : : }
2169 : :
2170 : :
2171 : : /*
2172 : : * datand - returns the arctan of arg1 (degrees)
2173 : : */
2174 : : Datum
2175 : 30 : datand(PG_FUNCTION_ARGS)
2176 : : {
2177 : 30 : float8 arg1 = PG_GETARG_FLOAT8(0);
2178 : : float8 result;
2179 : : volatile float8 atan_arg1;
2180 : :
2181 : : /* Per the POSIX spec, return NaN if the input is NaN */
2182 [ - + ]: 30 : if (isnan(arg1))
3005 tgl@sss.pgh.pa.us 2183 :UBC 0 : PG_RETURN_FLOAT8(get_float8_nan());
2184 : :
3004 tgl@sss.pgh.pa.us 2185 [ - + ]:CBC 30 : INIT_DEGREE_CONSTANTS();
2186 : :
2187 : : /*
2188 : : * The principal branch of the inverse tangent function maps all inputs to
2189 : : * values in the range [-90, 90], so the result should always be finite,
2190 : : * even if the input is infinite. Additionally, we take care to ensure
2191 : : * than when arg1 is 1, the result is exactly 45.
2192 : : */
2910 2193 : 30 : atan_arg1 = atan(arg1);
2194 : 30 : result = (atan_arg1 / atan_1_0) * 45.0;
2195 : :
1522 2196 [ - + ]: 30 : if (unlikely(isinf(result)))
1522 tgl@sss.pgh.pa.us 2197 :UBC 0 : float_overflow_error();
2198 : :
3005 tgl@sss.pgh.pa.us 2199 :CBC 30 : PG_RETURN_FLOAT8(result);
2200 : : }
2201 : :
2202 : :
2203 : : /*
2204 : : * atan2d - returns the arctan of arg1/arg2 (degrees)
2205 : : */
2206 : : Datum
2207 : 30 : datan2d(PG_FUNCTION_ARGS)
2208 : : {
2209 : 30 : float8 arg1 = PG_GETARG_FLOAT8(0);
2210 : 30 : float8 arg2 = PG_GETARG_FLOAT8(1);
2211 : : float8 result;
2212 : : volatile float8 atan2_arg1_arg2;
2213 : :
2214 : : /* Per the POSIX spec, return NaN if either input is NaN */
2215 [ + - - + ]: 30 : if (isnan(arg1) || isnan(arg2))
3005 tgl@sss.pgh.pa.us 2216 :UBC 0 : PG_RETURN_FLOAT8(get_float8_nan());
2217 : :
3004 tgl@sss.pgh.pa.us 2218 [ - + ]:CBC 30 : INIT_DEGREE_CONSTANTS();
2219 : :
2220 : : /*
2221 : : * atan2d maps all inputs to values in the range [-180, 180], so the
2222 : : * result should always be finite, even if the inputs are infinite.
2223 : : *
2224 : : * Note: this coding assumes that atan(1.0) is a suitable scaling constant
2225 : : * to get an exact result from atan2(). This might well fail on us at
2226 : : * some point, requiring us to decide exactly what inputs we think we're
2227 : : * going to guarantee an exact result for.
2228 : : */
2910 2229 : 30 : atan2_arg1_arg2 = atan2(arg1, arg2);
2230 : 30 : result = (atan2_arg1_arg2 / atan_1_0) * 45.0;
2231 : :
1522 2232 [ - + ]: 30 : if (unlikely(isinf(result)))
1522 tgl@sss.pgh.pa.us 2233 :UBC 0 : float_overflow_error();
2234 : :
3005 tgl@sss.pgh.pa.us 2235 :CBC 30 : PG_RETURN_FLOAT8(result);
2236 : : }
2237 : :
2238 : :
2239 : : /*
2240 : : * sind_0_to_30 - returns the sine of an angle that lies between 0 and
2241 : : * 30 degrees. This will return exactly 0 when x is 0,
2242 : : * and exactly 0.5 when x is 30 degrees.
2243 : : */
2244 : : static double
2245 : 159 : sind_0_to_30(double x)
2246 : : {
2910 2247 : 159 : volatile float8 sin_x = sin(x * RADIANS_PER_DEGREE);
2248 : :
2249 : 159 : return (sin_x / sin_30) / 2.0;
2250 : : }
2251 : :
2252 : :
2253 : : /*
2254 : : * cosd_0_to_60 - returns the cosine of an angle that lies between 0
2255 : : * and 60 degrees. This will return exactly 1 when x
2256 : : * is 0, and exactly 0.5 when x is 60 degrees.
2257 : : */
2258 : : static double
3005 2259 : 267 : cosd_0_to_60(double x)
2260 : : {
2910 2261 : 267 : volatile float8 one_minus_cos_x = 1.0 - cos(x * RADIANS_PER_DEGREE);
2262 : :
3003 2263 : 267 : return 1.0 - (one_minus_cos_x / one_minus_cos_60) / 2.0;
2264 : : }
2265 : :
2266 : :
2267 : : /*
2268 : : * sind_q1 - returns the sine of an angle in the first quadrant
2269 : : * (0 to 90 degrees).
2270 : : */
2271 : : static double
3005 2272 : 213 : sind_q1(double x)
2273 : : {
2274 : : /*
2275 : : * Stitch together the sine and cosine functions for the ranges [0, 30]
2276 : : * and (30, 90]. These guarantee to return exact answers at their
2277 : : * endpoints, so the overall result is a continuous monotonic function
2278 : : * that gives exact results when x = 0, 30 and 90 degrees.
2279 : : */
2280 [ + + ]: 213 : if (x <= 30.0)
2281 : 105 : return sind_0_to_30(x);
2282 : : else
2283 : 108 : return cosd_0_to_60(90.0 - x);
2284 : : }
2285 : :
2286 : :
2287 : : /*
2288 : : * cosd_q1 - returns the cosine of an angle in the first quadrant
2289 : : * (0 to 90 degrees).
2290 : : */
2291 : : static double
2292 : 213 : cosd_q1(double x)
2293 : : {
2294 : : /*
2295 : : * Stitch together the sine and cosine functions for the ranges [0, 60]
2296 : : * and (60, 90]. These guarantee to return exact answers at their
2297 : : * endpoints, so the overall result is a continuous monotonic function
2298 : : * that gives exact results when x = 0, 60 and 90 degrees.
2299 : : */
2300 [ + + ]: 213 : if (x <= 60.0)
2301 : 159 : return cosd_0_to_60(x);
2302 : : else
2303 : 54 : return sind_0_to_30(90.0 - x);
2304 : : }
2305 : :
2306 : :
2307 : : /*
2308 : : * dcosd - returns the cosine of arg1 (degrees)
2309 : : */
2310 : : Datum
2311 : 99 : dcosd(PG_FUNCTION_ARGS)
2312 : : {
2313 : 99 : float8 arg1 = PG_GETARG_FLOAT8(0);
2314 : : float8 result;
3004 2315 : 99 : int sign = 1;
2316 : :
2317 : : /*
2318 : : * Per the POSIX spec, return NaN if the input is NaN and throw an error
2319 : : * if the input is infinite.
2320 : : */
3005 2321 [ - + ]: 99 : if (isnan(arg1))
3005 tgl@sss.pgh.pa.us 2322 :UBC 0 : PG_RETURN_FLOAT8(get_float8_nan());
2323 : :
3005 tgl@sss.pgh.pa.us 2324 [ - + ]:CBC 99 : if (isinf(arg1))
3005 tgl@sss.pgh.pa.us 2325 [ # # ]:UBC 0 : ereport(ERROR,
2326 : : (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
2327 : : errmsg("input is out of range")));
2328 : :
3004 tgl@sss.pgh.pa.us 2329 [ - + ]:CBC 99 : INIT_DEGREE_CONSTANTS();
2330 : :
2331 : : /* Reduce the range of the input to [0,90] degrees */
3005 2332 : 99 : arg1 = fmod(arg1, 360.0);
2333 : :
2334 [ - + ]: 99 : if (arg1 < 0.0)
2335 : : {
2336 : : /* cosd(-x) = cosd(x) */
3005 tgl@sss.pgh.pa.us 2337 :UBC 0 : arg1 = -arg1;
2338 : : }
2339 : :
3005 tgl@sss.pgh.pa.us 2340 [ + + ]:CBC 99 : if (arg1 > 180.0)
2341 : : {
2342 : : /* cosd(360-x) = cosd(x) */
2343 : 27 : arg1 = 360.0 - arg1;
2344 : : }
2345 : :
2346 [ + + ]: 99 : if (arg1 > 90.0)
2347 : : {
2348 : : /* cosd(180-x) = -cosd(x) */
2349 : 27 : arg1 = 180.0 - arg1;
2350 : 27 : sign = -sign;
2351 : : }
2352 : :
2353 : 99 : result = sign * cosd_q1(arg1);
2354 : :
1522 2355 [ - + ]: 99 : if (unlikely(isinf(result)))
1522 tgl@sss.pgh.pa.us 2356 :UBC 0 : float_overflow_error();
2357 : :
3005 tgl@sss.pgh.pa.us 2358 :CBC 99 : PG_RETURN_FLOAT8(result);
2359 : : }
2360 : :
2361 : :
2362 : : /*
2363 : : * dcotd - returns the cotangent of arg1 (degrees)
2364 : : */
2365 : : Datum
2366 : 54 : dcotd(PG_FUNCTION_ARGS)
2367 : : {
2368 : 54 : float8 arg1 = PG_GETARG_FLOAT8(0);
2369 : : float8 result;
2370 : : volatile float8 cot_arg1;
3004 2371 : 54 : int sign = 1;
2372 : :
2373 : : /*
2374 : : * Per the POSIX spec, return NaN if the input is NaN and throw an error
2375 : : * if the input is infinite.
2376 : : */
3005 2377 [ - + ]: 54 : if (isnan(arg1))
3005 tgl@sss.pgh.pa.us 2378 :UBC 0 : PG_RETURN_FLOAT8(get_float8_nan());
2379 : :
3005 tgl@sss.pgh.pa.us 2380 [ - + ]:CBC 54 : if (isinf(arg1))
3005 tgl@sss.pgh.pa.us 2381 [ # # ]:UBC 0 : ereport(ERROR,
2382 : : (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
2383 : : errmsg("input is out of range")));
2384 : :
3004 tgl@sss.pgh.pa.us 2385 [ - + ]:CBC 54 : INIT_DEGREE_CONSTANTS();
2386 : :
2387 : : /* Reduce the range of the input to [0,90] degrees */
3005 2388 : 54 : arg1 = fmod(arg1, 360.0);
2389 : :
2390 [ - + ]: 54 : if (arg1 < 0.0)
2391 : : {
2392 : : /* cotd(-x) = -cotd(x) */
3005 tgl@sss.pgh.pa.us 2393 :UBC 0 : arg1 = -arg1;
2394 : 0 : sign = -sign;
2395 : : }
2396 : :
3005 tgl@sss.pgh.pa.us 2397 [ + + ]:CBC 54 : if (arg1 > 180.0)
2398 : : {
2399 : : /* cotd(360-x) = -cotd(x) */
2400 : 18 : arg1 = 360.0 - arg1;
2401 : 18 : sign = -sign;
2402 : : }
2403 : :
2404 [ + + ]: 54 : if (arg1 > 90.0)
2405 : : {
2406 : : /* cotd(180-x) = -cotd(x) */
2407 : 18 : arg1 = 180.0 - arg1;
2408 : 18 : sign = -sign;
2409 : : }
2410 : :
2910 2411 : 54 : cot_arg1 = cosd_q1(arg1) / sind_q1(arg1);
2412 : 54 : result = sign * (cot_arg1 / cot_45);
2413 : :
2414 : : /*
2415 : : * On some machines we get cotd(270) = minus zero, but this isn't always
2416 : : * true. For portability, and because the user constituency for this
2417 : : * function probably doesn't want minus zero, force it to plain zero.
2418 : : */
3004 2419 [ + + ]: 54 : if (result == 0.0)
2420 : 12 : result = 0.0;
2421 : :
2422 : : /* Not checking for overflow because cotd(0) == Inf */
2423 : :
3005 2424 : 54 : PG_RETURN_FLOAT8(result);
2425 : : }
2426 : :
2427 : :
2428 : : /*
2429 : : * dsind - returns the sine of arg1 (degrees)
2430 : : */
2431 : : Datum
2432 : 99 : dsind(PG_FUNCTION_ARGS)
2433 : : {
2434 : 99 : float8 arg1 = PG_GETARG_FLOAT8(0);
2435 : : float8 result;
3004 2436 : 99 : int sign = 1;
2437 : :
2438 : : /*
2439 : : * Per the POSIX spec, return NaN if the input is NaN and throw an error
2440 : : * if the input is infinite.
2441 : : */
3005 2442 [ - + ]: 99 : if (isnan(arg1))
3005 tgl@sss.pgh.pa.us 2443 :UBC 0 : PG_RETURN_FLOAT8(get_float8_nan());
2444 : :
3005 tgl@sss.pgh.pa.us 2445 [ - + ]:CBC 99 : if (isinf(arg1))
3005 tgl@sss.pgh.pa.us 2446 [ # # ]:UBC 0 : ereport(ERROR,
2447 : : (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
2448 : : errmsg("input is out of range")));
2449 : :
3004 tgl@sss.pgh.pa.us 2450 [ + + ]:CBC 99 : INIT_DEGREE_CONSTANTS();
2451 : :
2452 : : /* Reduce the range of the input to [0,90] degrees */
3005 2453 : 99 : arg1 = fmod(arg1, 360.0);
2454 : :
2455 [ - + ]: 99 : if (arg1 < 0.0)
2456 : : {
2457 : : /* sind(-x) = -sind(x) */
3005 tgl@sss.pgh.pa.us 2458 :UBC 0 : arg1 = -arg1;
2459 : 0 : sign = -sign;
2460 : : }
2461 : :
3005 tgl@sss.pgh.pa.us 2462 [ + + ]:CBC 99 : if (arg1 > 180.0)
2463 : : {
2464 : : /* sind(360-x) = -sind(x) */
2465 : 27 : arg1 = 360.0 - arg1;
2466 : 27 : sign = -sign;
2467 : : }
2468 : :
2469 [ + + ]: 99 : if (arg1 > 90.0)
2470 : : {
2471 : : /* sind(180-x) = sind(x) */
2472 : 27 : arg1 = 180.0 - arg1;
2473 : : }
2474 : :
2475 : 99 : result = sign * sind_q1(arg1);
2476 : :
1522 2477 [ - + ]: 99 : if (unlikely(isinf(result)))
1522 tgl@sss.pgh.pa.us 2478 :UBC 0 : float_overflow_error();
2479 : :
3005 tgl@sss.pgh.pa.us 2480 :CBC 99 : PG_RETURN_FLOAT8(result);
2481 : : }
2482 : :
2483 : :
2484 : : /*
2485 : : * dtand - returns the tangent of arg1 (degrees)
2486 : : */
2487 : : Datum
2488 : 54 : dtand(PG_FUNCTION_ARGS)
2489 : : {
2490 : 54 : float8 arg1 = PG_GETARG_FLOAT8(0);
2491 : : float8 result;
2492 : : volatile float8 tan_arg1;
3004 2493 : 54 : int sign = 1;
2494 : :
2495 : : /*
2496 : : * Per the POSIX spec, return NaN if the input is NaN and throw an error
2497 : : * if the input is infinite.
2498 : : */
3005 2499 [ - + ]: 54 : if (isnan(arg1))
3005 tgl@sss.pgh.pa.us 2500 :UBC 0 : PG_RETURN_FLOAT8(get_float8_nan());
2501 : :
3005 tgl@sss.pgh.pa.us 2502 [ - + ]:CBC 54 : if (isinf(arg1))
3005 tgl@sss.pgh.pa.us 2503 [ # # ]:UBC 0 : ereport(ERROR,
2504 : : (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
2505 : : errmsg("input is out of range")));
2506 : :
3004 tgl@sss.pgh.pa.us 2507 [ - + ]:CBC 54 : INIT_DEGREE_CONSTANTS();
2508 : :
2509 : : /* Reduce the range of the input to [0,90] degrees */
3005 2510 : 54 : arg1 = fmod(arg1, 360.0);
2511 : :
2512 [ - + ]: 54 : if (arg1 < 0.0)
2513 : : {
2514 : : /* tand(-x) = -tand(x) */
3005 tgl@sss.pgh.pa.us 2515 :UBC 0 : arg1 = -arg1;
2516 : 0 : sign = -sign;
2517 : : }
2518 : :
3005 tgl@sss.pgh.pa.us 2519 [ + + ]:CBC 54 : if (arg1 > 180.0)
2520 : : {
2521 : : /* tand(360-x) = -tand(x) */
2522 : 18 : arg1 = 360.0 - arg1;
2523 : 18 : sign = -sign;
2524 : : }
2525 : :
2526 [ + + ]: 54 : if (arg1 > 90.0)
2527 : : {
2528 : : /* tand(180-x) = -tand(x) */
2529 : 18 : arg1 = 180.0 - arg1;
2530 : 18 : sign = -sign;
2531 : : }
2532 : :
2910 2533 : 54 : tan_arg1 = sind_q1(arg1) / cosd_q1(arg1);
2534 : 54 : result = sign * (tan_arg1 / tan_45);
2535 : :
2536 : : /*
2537 : : * On some machines we get tand(180) = minus zero, but this isn't always
2538 : : * true. For portability, and because the user constituency for this
2539 : : * function probably doesn't want minus zero, force it to plain zero.
2540 : : */
3004 2541 [ + + ]: 54 : if (result == 0.0)
2542 : 18 : result = 0.0;
2543 : :
2544 : : /* Not checking for overflow because tand(90) == Inf */
2545 : :
3005 2546 : 54 : PG_RETURN_FLOAT8(result);
2547 : : }
2548 : :
2549 : :
2550 : : /*
2551 : : * degrees - returns degrees converted from radians
2552 : : */
2553 : : Datum
8657 2554 : 40 : degrees(PG_FUNCTION_ARGS)
2555 : : {
2556 : 40 : float8 arg1 = PG_GETARG_FLOAT8(0);
2557 : :
2086 tomas.vondra@postgre 2558 : 40 : PG_RETURN_FLOAT8(float8_div(arg1, RADIANS_PER_DEGREE));
2559 : : }
2560 : :
2561 : :
2562 : : /*
2563 : : * dpi - returns the constant PI
2564 : : */
2565 : : Datum
8657 tgl@sss.pgh.pa.us 2566 : 101 : dpi(PG_FUNCTION_ARGS)
2567 : : {
2568 : 101 : PG_RETURN_FLOAT8(M_PI);
2569 : : }
2570 : :
2571 : :
2572 : : /*
2573 : : * radians - returns radians converted from degrees
2574 : : */
2575 : : Datum
2576 : 955 : radians(PG_FUNCTION_ARGS)
2577 : : {
2578 : 955 : float8 arg1 = PG_GETARG_FLOAT8(0);
2579 : :
2086 tomas.vondra@postgre 2580 : 955 : PG_RETURN_FLOAT8(float8_mul(arg1, RADIANS_PER_DEGREE));
2581 : : }
2582 : :
2583 : :
2584 : : /* ========== HYPERBOLIC FUNCTIONS ========== */
2585 : :
2586 : :
2587 : : /*
2588 : : * dsinh - returns the hyperbolic sine of arg1
2589 : : */
2590 : : Datum
1860 tgl@sss.pgh.pa.us 2591 : 12 : dsinh(PG_FUNCTION_ARGS)
2592 : : {
2593 : 12 : float8 arg1 = PG_GETARG_FLOAT8(0);
2594 : : float8 result;
2595 : :
2596 : 12 : errno = 0;
2597 : 12 : result = sinh(arg1);
2598 : :
2599 : : /*
2600 : : * if an ERANGE error occurs, it means there is an overflow. For sinh,
2601 : : * the result should be either -infinity or infinity, depending on the
2602 : : * sign of arg1.
2603 : : */
2604 [ - + ]: 12 : if (errno == ERANGE)
2605 : : {
1860 tgl@sss.pgh.pa.us 2606 [ # # ]:UBC 0 : if (arg1 < 0)
2607 : 0 : result = -get_float8_infinity();
2608 : : else
2609 : 0 : result = get_float8_infinity();
2610 : : }
2611 : :
1860 tgl@sss.pgh.pa.us 2612 :CBC 12 : PG_RETURN_FLOAT8(result);
2613 : : }
2614 : :
2615 : :
2616 : : /*
2617 : : * dcosh - returns the hyperbolic cosine of arg1
2618 : : */
2619 : : Datum
2620 : 12 : dcosh(PG_FUNCTION_ARGS)
2621 : : {
2622 : 12 : float8 arg1 = PG_GETARG_FLOAT8(0);
2623 : : float8 result;
2624 : :
2625 : 12 : errno = 0;
2626 : 12 : result = cosh(arg1);
2627 : :
2628 : : /*
2629 : : * if an ERANGE error occurs, it means there is an overflow. As cosh is
2630 : : * always positive, it always means the result is positive infinity.
2631 : : */
2632 [ - + ]: 12 : if (errno == ERANGE)
1860 tgl@sss.pgh.pa.us 2633 :UBC 0 : result = get_float8_infinity();
2634 : :
1522 tgl@sss.pgh.pa.us 2635 [ - + ]:CBC 12 : if (unlikely(result == 0.0))
1522 tgl@sss.pgh.pa.us 2636 :UBC 0 : float_underflow_error();
2637 : :
1860 tgl@sss.pgh.pa.us 2638 :CBC 12 : PG_RETURN_FLOAT8(result);
2639 : : }
2640 : :
2641 : : /*
2642 : : * dtanh - returns the hyperbolic tangent of arg1
2643 : : */
2644 : : Datum
2645 : 12 : dtanh(PG_FUNCTION_ARGS)
2646 : : {
2647 : 12 : float8 arg1 = PG_GETARG_FLOAT8(0);
2648 : : float8 result;
2649 : :
2650 : : /*
2651 : : * For tanh, we don't need an errno check because it never overflows.
2652 : : */
2653 : 12 : result = tanh(arg1);
2654 : :
1522 2655 [ - + ]: 12 : if (unlikely(isinf(result)))
1522 tgl@sss.pgh.pa.us 2656 :UBC 0 : float_overflow_error();
2657 : :
1860 tgl@sss.pgh.pa.us 2658 :CBC 12 : PG_RETURN_FLOAT8(result);
2659 : : }
2660 : :
2661 : : /*
2662 : : * dasinh - returns the inverse hyperbolic sine of arg1
2663 : : */
2664 : : Datum
2665 : 12 : dasinh(PG_FUNCTION_ARGS)
2666 : : {
2667 : 12 : float8 arg1 = PG_GETARG_FLOAT8(0);
2668 : : float8 result;
2669 : :
2670 : : /*
2671 : : * For asinh, we don't need an errno check because it never overflows.
2672 : : */
2673 : 12 : result = asinh(arg1);
2674 : :
2675 : 12 : PG_RETURN_FLOAT8(result);
2676 : : }
2677 : :
2678 : : /*
2679 : : * dacosh - returns the inverse hyperbolic cosine of arg1
2680 : : */
2681 : : Datum
2682 : 9 : dacosh(PG_FUNCTION_ARGS)
2683 : : {
2684 : 9 : float8 arg1 = PG_GETARG_FLOAT8(0);
2685 : : float8 result;
2686 : :
2687 : : /*
2688 : : * acosh is only defined for inputs >= 1.0. By checking this ourselves,
2689 : : * we need not worry about checking for an EDOM error, which is a good
2690 : : * thing because some implementations will report that for NaN. Otherwise,
2691 : : * no error is possible.
2692 : : */
2693 [ + + ]: 9 : if (arg1 < 1.0)
2694 [ + - ]: 3 : ereport(ERROR,
2695 : : (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
2696 : : errmsg("input is out of range")));
2697 : :
2698 : 6 : result = acosh(arg1);
2699 : :
2700 : 6 : PG_RETURN_FLOAT8(result);
2701 : : }
2702 : :
2703 : : /*
2704 : : * datanh - returns the inverse hyperbolic tangent of arg1
2705 : : */
2706 : : Datum
2707 : 12 : datanh(PG_FUNCTION_ARGS)
2708 : : {
2709 : 12 : float8 arg1 = PG_GETARG_FLOAT8(0);
2710 : : float8 result;
2711 : :
2712 : : /*
2713 : : * atanh is only defined for inputs between -1 and 1. By checking this
2714 : : * ourselves, we need not worry about checking for an EDOM error, which is
2715 : : * a good thing because some implementations will report that for NaN.
2716 : : */
2717 [ + + + + ]: 12 : if (arg1 < -1.0 || arg1 > 1.0)
2718 [ + - ]: 6 : ereport(ERROR,
2719 : : (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
2720 : : errmsg("input is out of range")));
2721 : :
2722 : : /*
2723 : : * Also handle the infinity cases ourselves; this is helpful because old
2724 : : * glibc versions may produce the wrong errno for this. All other inputs
2725 : : * cannot produce an error.
2726 : : */
2727 [ - + ]: 6 : if (arg1 == -1.0)
1860 tgl@sss.pgh.pa.us 2728 :UBC 0 : result = -get_float8_infinity();
1860 tgl@sss.pgh.pa.us 2729 [ - + ]:CBC 6 : else if (arg1 == 1.0)
1860 tgl@sss.pgh.pa.us 2730 :UBC 0 : result = get_float8_infinity();
2731 : : else
1860 tgl@sss.pgh.pa.us 2732 :CBC 6 : result = atanh(arg1);
2733 : :
2734 : 6 : PG_RETURN_FLOAT8(result);
2735 : : }
2736 : :
2737 : :
2738 : : /* ========== ERROR FUNCTIONS ========== */
2739 : :
2740 : :
2741 : : /*
2742 : : * derf - returns the error function: erf(arg1)
2743 : : */
2744 : : Datum
397 dean.a.rasheed@gmail 2745 : 3066 : derf(PG_FUNCTION_ARGS)
2746 : : {
2747 : 3066 : float8 arg1 = PG_GETARG_FLOAT8(0);
2748 : : float8 result;
2749 : :
2750 : : /*
2751 : : * For erf, we don't need an errno check because it never overflows.
2752 : : */
2753 : 3066 : result = erf(arg1);
2754 : :
2755 [ - + ]: 3066 : if (unlikely(isinf(result)))
397 dean.a.rasheed@gmail 2756 :UBC 0 : float_overflow_error();
2757 : :
397 dean.a.rasheed@gmail 2758 :CBC 3066 : PG_RETURN_FLOAT8(result);
2759 : : }
2760 : :
2761 : : /*
2762 : : * derfc - returns the complementary error function: 1 - erf(arg1)
2763 : : */
2764 : : Datum
2765 : 66 : derfc(PG_FUNCTION_ARGS)
2766 : : {
2767 : 66 : float8 arg1 = PG_GETARG_FLOAT8(0);
2768 : : float8 result;
2769 : :
2770 : : /*
2771 : : * For erfc, we don't need an errno check because it never overflows.
2772 : : */
2773 : 66 : result = erfc(arg1);
2774 : :
2775 [ - + ]: 66 : if (unlikely(isinf(result)))
397 dean.a.rasheed@gmail 2776 :UBC 0 : float_overflow_error();
2777 : :
397 dean.a.rasheed@gmail 2778 :CBC 66 : PG_RETURN_FLOAT8(result);
2779 : : }
2780 : :
2781 : :
2782 : :
2783 : : /*
2784 : : * =========================
2785 : : * FLOAT AGGREGATE OPERATORS
2786 : : * =========================
2787 : : *
2788 : : * float8_accum - accumulate for AVG(), variance aggregates, etc.
2789 : : * float4_accum - same, but input data is float4
2790 : : * float8_avg - produce final result for float AVG()
2791 : : * float8_var_samp - produce final result for float VAR_SAMP()
2792 : : * float8_var_pop - produce final result for float VAR_POP()
2793 : : * float8_stddev_samp - produce final result for float STDDEV_SAMP()
2794 : : * float8_stddev_pop - produce final result for float STDDEV_POP()
2795 : : *
2796 : : * The naive schoolbook implementation of these aggregates works by
2797 : : * accumulating sum(X) and sum(X^2). However, this approach suffers from
2798 : : * large rounding errors in the final computation of quantities like the
2799 : : * population variance (N*sum(X^2) - sum(X)^2) / N^2, since each of the
2800 : : * intermediate terms is potentially very large, while the difference is often
2801 : : * quite small.
2802 : : *
2803 : : * Instead we use the Youngs-Cramer algorithm [1] which works by accumulating
2804 : : * Sx=sum(X) and Sxx=sum((X-Sx/N)^2), using a numerically stable algorithm to
2805 : : * incrementally update those quantities. The final computations of each of
2806 : : * the aggregate values is then trivial and gives more accurate results (for
2807 : : * example, the population variance is just Sxx/N). This algorithm is also
2808 : : * fairly easy to generalize to allow parallel execution without loss of
2809 : : * precision (see, for example, [2]). For more details, and a comparison of
2810 : : * this with other algorithms, see [3].
2811 : : *
2812 : : * The transition datatype for all these aggregates is a 3-element array
2813 : : * of float8, holding the values N, Sx, Sxx in that order.
2814 : : *
2815 : : * Note that we represent N as a float to avoid having to build a special
2816 : : * datatype. Given a reasonable floating-point implementation, there should
2817 : : * be no accuracy loss unless N exceeds 2 ^ 52 or so (by which time the
2818 : : * user will have doubtless lost interest anyway...)
2819 : : *
2820 : : * [1] Some Results Relevant to Choice of Sum and Sum-of-Product Algorithms,
2821 : : * E. A. Youngs and E. M. Cramer, Technometrics Vol 13, No 3, August 1971.
2822 : : *
2823 : : * [2] Updating Formulae and a Pairwise Algorithm for Computing Sample
2824 : : * Variances, T. F. Chan, G. H. Golub & R. J. LeVeque, COMPSTAT 1982.
2825 : : *
2826 : : * [3] Numerically Stable Parallel Computation of (Co-)Variance, Erich
2827 : : * Schubert and Michael Gertz, Proceedings of the 30th International
2828 : : * Conference on Scientific and Statistical Database Management, 2018.
2829 : : */
2830 : :
2831 : : static float8 *
6470 tgl@sss.pgh.pa.us 2832 : 746 : check_float8_array(ArrayType *transarray, const char *caller, int n)
2833 : : {
2834 : : /*
2835 : : * We expect the input to be an N-element float array; verify that. We
2836 : : * don't need to use deconstruct_array() since the array data is just
2837 : : * going to look like a C array of N float8 values.
2838 : : */
7902 2839 [ + - ]: 746 : if (ARR_NDIM(transarray) != 1 ||
6470 2840 [ + - ]: 746 : ARR_DIMS(transarray)[0] != n ||
6723 2841 [ + - ]: 746 : ARR_HASNULL(transarray) ||
7902 2842 [ - + ]: 746 : ARR_ELEMTYPE(transarray) != FLOAT8OID)
6470 tgl@sss.pgh.pa.us 2843 [ # # ]:UBC 0 : elog(ERROR, "%s: expected %d-element float8 array", caller, n);
8672 tgl@sss.pgh.pa.us 2844 [ - + ]:CBC 746 : return (float8 *) ARR_DATA_PTR(transarray);
2845 : : }
2846 : :
2847 : : /*
2848 : : * float8_combine
2849 : : *
2850 : : * An aggregate combine function used to combine two 3 fields
2851 : : * aggregate transition data into a single transition data.
2852 : : * This function is used only in two stage aggregation and
2853 : : * shouldn't be called outside aggregate context.
2854 : : */
2855 : : Datum
2928 rhaas@postgresql.org 2856 : 9 : float8_combine(PG_FUNCTION_ARGS)
2857 : : {
2858 : 9 : ArrayType *transarray1 = PG_GETARG_ARRAYTYPE_P(0);
2859 : 9 : ArrayType *transarray2 = PG_GETARG_ARRAYTYPE_P(1);
2860 : : float8 *transvalues1;
2861 : : float8 *transvalues2;
2862 : : float8 N1,
2863 : : Sx1,
2864 : : Sxx1,
2865 : : N2,
2866 : : Sx2,
2867 : : Sxx2,
2868 : : tmp,
2869 : : N,
2870 : : Sx,
2871 : : Sxx;
2872 : :
2873 : 9 : transvalues1 = check_float8_array(transarray1, "float8_combine", 3);
2874 : 9 : transvalues2 = check_float8_array(transarray2, "float8_combine", 3);
2875 : :
2017 dean.a.rasheed@gmail 2876 : 9 : N1 = transvalues1[0];
2877 : 9 : Sx1 = transvalues1[1];
2878 : 9 : Sxx1 = transvalues1[2];
2879 : :
2880 : 9 : N2 = transvalues2[0];
2881 : 9 : Sx2 = transvalues2[1];
2882 : 9 : Sxx2 = transvalues2[2];
2883 : :
2884 : : /*--------------------
2885 : : * The transition values combine using a generalization of the
2886 : : * Youngs-Cramer algorithm as follows:
2887 : : *
2888 : : * N = N1 + N2
2889 : : * Sx = Sx1 + Sx2
2890 : : * Sxx = Sxx1 + Sxx2 + N1 * N2 * (Sx1/N1 - Sx2/N2)^2 / N;
2891 : : *
2892 : : * It's worth handling the special cases N1 = 0 and N2 = 0 separately
2893 : : * since those cases are trivial, and we then don't need to worry about
2894 : : * division-by-zero errors in the general case.
2895 : : *--------------------
2896 : : */
2897 [ + + ]: 9 : if (N1 == 0.0)
2898 : : {
2899 : 3 : N = N2;
2900 : 3 : Sx = Sx2;
2901 : 3 : Sxx = Sxx2;
2902 : : }
2903 [ + + ]: 6 : else if (N2 == 0.0)
2904 : : {
2905 : 3 : N = N1;
2906 : 3 : Sx = Sx1;
2907 : 3 : Sxx = Sxx1;
2908 : : }
2909 : : else
2910 : : {
2911 : 3 : N = N1 + N2;
2912 : 3 : Sx = float8_pl(Sx1, Sx2);
2913 : 3 : tmp = Sx1 / N1 - Sx2 / N2;
2914 : 3 : Sxx = Sxx1 + Sxx2 + N1 * N2 * tmp * tmp / N;
1522 tgl@sss.pgh.pa.us 2915 [ - + - - : 3 : if (unlikely(isinf(Sxx)) && !isinf(Sxx1) && !isinf(Sxx2))
- - ]
1522 tgl@sss.pgh.pa.us 2916 :UBC 0 : float_overflow_error();
2917 : : }
2918 : :
2919 : : /*
2920 : : * If we're invoked as an aggregate, we can cheat and modify our first
2921 : : * parameter in-place to reduce palloc overhead. Otherwise we construct a
2922 : : * new array with the updated transition data and return it.
2923 : : */
2017 dean.a.rasheed@gmail 2924 [ - + ]:CBC 9 : if (AggCheckCallContext(fcinfo, NULL))
2925 : : {
2017 dean.a.rasheed@gmail 2926 :UBC 0 : transvalues1[0] = N;
2927 : 0 : transvalues1[1] = Sx;
2928 : 0 : transvalues1[2] = Sxx;
2929 : :
2930 : 0 : PG_RETURN_ARRAYTYPE_P(transarray1);
2931 : : }
2932 : : else
2933 : : {
2934 : : Datum transdatums[3];
2935 : : ArrayType *result;
2936 : :
2017 dean.a.rasheed@gmail 2937 :CBC 9 : transdatums[0] = Float8GetDatumFast(N);
2938 : 9 : transdatums[1] = Float8GetDatumFast(Sx);
2939 : 9 : transdatums[2] = Float8GetDatumFast(Sxx);
2940 : :
2941 : 9 : result = construct_array(transdatums, 3,
2942 : : FLOAT8OID,
2943 : : sizeof(float8), FLOAT8PASSBYVAL, TYPALIGN_DOUBLE);
2944 : :
2945 : 9 : PG_RETURN_ARRAYTYPE_P(result);
2946 : : }
2947 : : }
2948 : :
2949 : : Datum
8672 tgl@sss.pgh.pa.us 2950 : 181 : float8_accum(PG_FUNCTION_ARGS)
2951 : : {
2952 : 181 : ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0);
2953 : 181 : float8 newval = PG_GETARG_FLOAT8(1);
2954 : : float8 *transvalues;
2955 : : float8 N,
2956 : : Sx,
2957 : : Sxx,
2958 : : tmp;
2959 : :
6470 2960 : 181 : transvalues = check_float8_array(transarray, "float8_accum", 3);
2017 dean.a.rasheed@gmail 2961 : 181 : N = transvalues[0];
2962 : 181 : Sx = transvalues[1];
2963 : 181 : Sxx = transvalues[2];
2964 : :
2965 : : /*
2966 : : * Use the Youngs-Cramer algorithm to incorporate the new value into the
2967 : : * transition values.
2968 : : */
2969 : 181 : N += 1.0;
2970 : 181 : Sx += newval;
2971 [ + + ]: 181 : if (transvalues[0] > 0.0)
2972 : : {
2973 : 132 : tmp = newval * N - Sx;
2974 : 132 : Sxx += tmp * tmp / (N * transvalues[0]);
2975 : :
2976 : : /*
2977 : : * Overflow check. We only report an overflow error when finite
2978 : : * inputs lead to infinite results. Note also that Sxx should be NaN
2979 : : * if any of the inputs are infinite, so we intentionally prevent Sxx
2980 : : * from becoming infinite.
2981 : : */
2982 [ + + - + ]: 132 : if (isinf(Sx) || isinf(Sxx))
2983 : : {
2984 [ + + + + : 12 : if (!isinf(transvalues[1]) && !isinf(newval))
- + ]
1522 tgl@sss.pgh.pa.us 2985 :UBC 0 : float_overflow_error();
2986 : :
2017 dean.a.rasheed@gmail 2987 :CBC 12 : Sxx = get_float8_nan();
2988 : : }
2989 : : }
2990 : : else
2991 : : {
2992 : : /*
2993 : : * At the first input, we normally can leave Sxx as 0. However, if
2994 : : * the first input is Inf or NaN, we'd better force Sxx to NaN;
2995 : : * otherwise we will falsely report variance zero when there are no
2996 : : * more inputs.
2997 : : */
1401 tgl@sss.pgh.pa.us 2998 [ + + + + ]: 49 : if (isnan(newval) || isinf(newval))
2999 : 24 : Sxx = get_float8_nan();
3000 : : }
3001 : :
3002 : : /*
3003 : : * If we're invoked as an aggregate, we can cheat and modify our first
3004 : : * parameter in-place to reduce palloc overhead. Otherwise we construct a
3005 : : * new array with the updated transition data and return it.
3006 : : */
5179 3007 [ + + ]: 181 : if (AggCheckCallContext(fcinfo, NULL))
3008 : : {
2017 dean.a.rasheed@gmail 3009 : 178 : transvalues[0] = N;
3010 : 178 : transvalues[1] = Sx;
3011 : 178 : transvalues[2] = Sxx;
3012 : :
6948 neilc@samurai.com 3013 : 178 : PG_RETURN_ARRAYTYPE_P(transarray);
3014 : : }
3015 : : else
3016 : : {
3017 : : Datum transdatums[3];
3018 : : ArrayType *result;
3019 : :
2017 dean.a.rasheed@gmail 3020 : 3 : transdatums[0] = Float8GetDatumFast(N);
3021 : 3 : transdatums[1] = Float8GetDatumFast(Sx);
3022 : 3 : transdatums[2] = Float8GetDatumFast(Sxx);
3023 : :
6948 neilc@samurai.com 3024 : 3 : result = construct_array(transdatums, 3,
3025 : : FLOAT8OID,
3026 : : sizeof(float8), FLOAT8PASSBYVAL, TYPALIGN_DOUBLE);
3027 : :
3028 : 3 : PG_RETURN_ARRAYTYPE_P(result);
3029 : : }
3030 : : }
3031 : :
3032 : : Datum
8672 tgl@sss.pgh.pa.us 3033 : 144 : float4_accum(PG_FUNCTION_ARGS)
3034 : : {
3035 : 144 : ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0);
3036 : :
3037 : : /* do computations as float8 */
6312 bruce@momjian.us 3038 : 144 : float8 newval = PG_GETARG_FLOAT4(1);
3039 : : float8 *transvalues;
3040 : : float8 N,
3041 : : Sx,
3042 : : Sxx,
3043 : : tmp;
3044 : :
6470 tgl@sss.pgh.pa.us 3045 : 144 : transvalues = check_float8_array(transarray, "float4_accum", 3);
2017 dean.a.rasheed@gmail 3046 : 144 : N = transvalues[0];
3047 : 144 : Sx = transvalues[1];
3048 : 144 : Sxx = transvalues[2];
3049 : :
3050 : : /*
3051 : : * Use the Youngs-Cramer algorithm to incorporate the new value into the
3052 : : * transition values.
3053 : : */
3054 : 144 : N += 1.0;
3055 : 144 : Sx += newval;
3056 [ + + ]: 144 : if (transvalues[0] > 0.0)
3057 : : {
3058 : 102 : tmp = newval * N - Sx;
3059 : 102 : Sxx += tmp * tmp / (N * transvalues[0]);
3060 : :
3061 : : /*
3062 : : * Overflow check. We only report an overflow error when finite
3063 : : * inputs lead to infinite results. Note also that Sxx should be NaN
3064 : : * if any of the inputs are infinite, so we intentionally prevent Sxx
3065 : : * from becoming infinite.
3066 : : */
3067 [ + - - + ]: 102 : if (isinf(Sx) || isinf(Sxx))
3068 : : {
2017 dean.a.rasheed@gmail 3069 [ # # # # :UBC 0 : if (!isinf(transvalues[1]) && !isinf(newval))
# # ]
1522 tgl@sss.pgh.pa.us 3070 : 0 : float_overflow_error();
3071 : :
2017 dean.a.rasheed@gmail 3072 : 0 : Sxx = get_float8_nan();
3073 : : }
3074 : : }
3075 : : else
3076 : : {
3077 : : /*
3078 : : * At the first input, we normally can leave Sxx as 0. However, if
3079 : : * the first input is Inf or NaN, we'd better force Sxx to NaN;
3080 : : * otherwise we will falsely report variance zero when there are no
3081 : : * more inputs.
3082 : : */
1401 tgl@sss.pgh.pa.us 3083 [ + + + + ]:CBC 42 : if (isnan(newval) || isinf(newval))
3084 : 12 : Sxx = get_float8_nan();
3085 : : }
3086 : :
3087 : : /*
3088 : : * If we're invoked as an aggregate, we can cheat and modify our first
3089 : : * parameter in-place to reduce palloc overhead. Otherwise we construct a
3090 : : * new array with the updated transition data and return it.
3091 : : */
5179 3092 [ + - ]: 144 : if (AggCheckCallContext(fcinfo, NULL))
3093 : : {
2017 dean.a.rasheed@gmail 3094 : 144 : transvalues[0] = N;
3095 : 144 : transvalues[1] = Sx;
3096 : 144 : transvalues[2] = Sxx;
3097 : :
6948 neilc@samurai.com 3098 : 144 : PG_RETURN_ARRAYTYPE_P(transarray);
3099 : : }
3100 : : else
3101 : : {
3102 : : Datum transdatums[3];
3103 : : ArrayType *result;
3104 : :
2017 dean.a.rasheed@gmail 3105 :UBC 0 : transdatums[0] = Float8GetDatumFast(N);
3106 : 0 : transdatums[1] = Float8GetDatumFast(Sx);
3107 : 0 : transdatums[2] = Float8GetDatumFast(Sxx);
3108 : :
6948 neilc@samurai.com 3109 : 0 : result = construct_array(transdatums, 3,
3110 : : FLOAT8OID,
3111 : : sizeof(float8), FLOAT8PASSBYVAL, TYPALIGN_DOUBLE);
3112 : :
3113 : 0 : PG_RETURN_ARRAYTYPE_P(result);
3114 : : }
3115 : : }
3116 : :
3117 : : Datum
8672 tgl@sss.pgh.pa.us 3118 :CBC 31 : float8_avg(PG_FUNCTION_ARGS)
3119 : : {
3120 : 31 : ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0);
3121 : : float8 *transvalues;
3122 : : float8 N,
3123 : : Sx;
3124 : :
6470 3125 : 31 : transvalues = check_float8_array(transarray, "float8_avg", 3);
8672 3126 : 31 : N = transvalues[0];
2017 dean.a.rasheed@gmail 3127 : 31 : Sx = transvalues[1];
3128 : : /* ignore Sxx */
3129 : :
3130 : : /* SQL defines AVG of no values to be NULL */
8672 tgl@sss.pgh.pa.us 3131 [ + + ]: 31 : if (N == 0.0)
3132 : 3 : PG_RETURN_NULL();
3133 : :
2017 dean.a.rasheed@gmail 3134 : 28 : PG_RETURN_FLOAT8(Sx / N);
3135 : : }
3136 : :
3137 : : Datum
6610 neilc@samurai.com 3138 : 42 : float8_var_pop(PG_FUNCTION_ARGS)
3139 : : {
8672 tgl@sss.pgh.pa.us 3140 : 42 : ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0);
3141 : : float8 *transvalues;
3142 : : float8 N,
3143 : : Sxx;
3144 : :
6470 3145 : 42 : transvalues = check_float8_array(transarray, "float8_var_pop", 3);
6610 neilc@samurai.com 3146 : 42 : N = transvalues[0];
3147 : : /* ignore Sx */
2017 dean.a.rasheed@gmail 3148 : 42 : Sxx = transvalues[2];
3149 : :
3150 : : /* Population variance is undefined when N is 0, so return NULL */
6610 neilc@samurai.com 3151 [ - + ]: 42 : if (N == 0.0)
6610 neilc@samurai.com 3152 :UBC 0 : PG_RETURN_NULL();
3153 : :
3154 : : /* Note that Sxx is guaranteed to be non-negative */
3155 : :
2017 dean.a.rasheed@gmail 3156 :CBC 42 : PG_RETURN_FLOAT8(Sxx / N);
3157 : : }
3158 : :
3159 : : Datum
6610 neilc@samurai.com 3160 : 21 : float8_var_samp(PG_FUNCTION_ARGS)
3161 : : {
3162 : 21 : ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0);
3163 : : float8 *transvalues;
3164 : : float8 N,
3165 : : Sxx;
3166 : :
6470 tgl@sss.pgh.pa.us 3167 : 21 : transvalues = check_float8_array(transarray, "float8_var_samp", 3);
8672 3168 : 21 : N = transvalues[0];
3169 : : /* ignore Sx */
2017 dean.a.rasheed@gmail 3170 : 21 : Sxx = transvalues[2];
3171 : :
3172 : : /* Sample variance is undefined when N is 0 or 1, so return NULL */
8672 tgl@sss.pgh.pa.us 3173 [ + + ]: 21 : if (N <= 1.0)
7664 3174 : 18 : PG_RETURN_NULL();
3175 : :
3176 : : /* Note that Sxx is guaranteed to be non-negative */
3177 : :
2017 dean.a.rasheed@gmail 3178 : 3 : PG_RETURN_FLOAT8(Sxx / (N - 1.0));
3179 : : }
3180 : :
3181 : : Datum
6610 neilc@samurai.com 3182 : 21 : float8_stddev_pop(PG_FUNCTION_ARGS)
3183 : : {
3184 : 21 : ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0);
3185 : : float8 *transvalues;
3186 : : float8 N,
3187 : : Sxx;
3188 : :
6470 tgl@sss.pgh.pa.us 3189 : 21 : transvalues = check_float8_array(transarray, "float8_stddev_pop", 3);
6610 neilc@samurai.com 3190 : 21 : N = transvalues[0];
3191 : : /* ignore Sx */
2017 dean.a.rasheed@gmail 3192 : 21 : Sxx = transvalues[2];
3193 : :
3194 : : /* Population stddev is undefined when N is 0, so return NULL */
6610 neilc@samurai.com 3195 [ - + ]: 21 : if (N == 0.0)
6610 neilc@samurai.com 3196 :UBC 0 : PG_RETURN_NULL();
3197 : :
3198 : : /* Note that Sxx is guaranteed to be non-negative */
3199 : :
2017 dean.a.rasheed@gmail 3200 :CBC 21 : PG_RETURN_FLOAT8(sqrt(Sxx / N));
3201 : : }
3202 : :
3203 : : Datum
6610 neilc@samurai.com 3204 : 24 : float8_stddev_samp(PG_FUNCTION_ARGS)
3205 : : {
8672 tgl@sss.pgh.pa.us 3206 : 24 : ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0);
3207 : : float8 *transvalues;
3208 : : float8 N,
3209 : : Sxx;
3210 : :
6470 3211 : 24 : transvalues = check_float8_array(transarray, "float8_stddev_samp", 3);
8672 3212 : 24 : N = transvalues[0];
3213 : : /* ignore Sx */
2017 dean.a.rasheed@gmail 3214 : 24 : Sxx = transvalues[2];
3215 : :
3216 : : /* Sample stddev is undefined when N is 0 or 1, so return NULL */
8672 tgl@sss.pgh.pa.us 3217 [ + + ]: 24 : if (N <= 1.0)
7664 3218 : 18 : PG_RETURN_NULL();
3219 : :
3220 : : /* Note that Sxx is guaranteed to be non-negative */
3221 : :
2017 dean.a.rasheed@gmail 3222 : 6 : PG_RETURN_FLOAT8(sqrt(Sxx / (N - 1.0)));
3223 : : }
3224 : :
3225 : : /*
3226 : : * =========================
3227 : : * SQL2003 BINARY AGGREGATES
3228 : : * =========================
3229 : : *
3230 : : * As with the preceding aggregates, we use the Youngs-Cramer algorithm to
3231 : : * reduce rounding errors in the aggregate final functions.
3232 : : *
3233 : : * The transition datatype for all these aggregates is a 6-element array of
3234 : : * float8, holding the values N, Sx=sum(X), Sxx=sum((X-Sx/N)^2), Sy=sum(Y),
3235 : : * Syy=sum((Y-Sy/N)^2), Sxy=sum((X-Sx/N)*(Y-Sy/N)) in that order.
3236 : : *
3237 : : * Note that Y is the first argument to all these aggregates!
3238 : : *
3239 : : * It might seem attractive to optimize this by having multiple accumulator
3240 : : * functions that only calculate the sums actually needed. But on most
3241 : : * modern machines, a couple of extra floating-point multiplies will be
3242 : : * insignificant compared to the other per-tuple overhead, so I've chosen
3243 : : * to minimize code space instead.
3244 : : */
3245 : :
3246 : : Datum
6470 tgl@sss.pgh.pa.us 3247 : 159 : float8_regr_accum(PG_FUNCTION_ARGS)
3248 : : {
3249 : 159 : ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0);
3250 : 159 : float8 newvalY = PG_GETARG_FLOAT8(1);
3251 : 159 : float8 newvalX = PG_GETARG_FLOAT8(2);
3252 : : float8 *transvalues;
3253 : : float8 N,
3254 : : Sx,
3255 : : Sxx,
3256 : : Sy,
3257 : : Syy,
3258 : : Sxy,
3259 : : tmpX,
3260 : : tmpY,
3261 : : scale;
3262 : :
3263 : 159 : transvalues = check_float8_array(transarray, "float8_regr_accum", 6);
3264 : 159 : N = transvalues[0];
2017 dean.a.rasheed@gmail 3265 : 159 : Sx = transvalues[1];
3266 : 159 : Sxx = transvalues[2];
3267 : 159 : Sy = transvalues[3];
3268 : 159 : Syy = transvalues[4];
3269 : 159 : Sxy = transvalues[5];
3270 : :
3271 : : /*
3272 : : * Use the Youngs-Cramer algorithm to incorporate the new values into the
3273 : : * transition values.
3274 : : */
6470 tgl@sss.pgh.pa.us 3275 : 159 : N += 1.0;
2017 dean.a.rasheed@gmail 3276 : 159 : Sx += newvalX;
3277 : 159 : Sy += newvalY;
3278 [ + + ]: 159 : if (transvalues[0] > 0.0)
3279 : : {
3280 : 105 : tmpX = newvalX * N - Sx;
3281 : 105 : tmpY = newvalY * N - Sy;
3282 : 105 : scale = 1.0 / (N * transvalues[0]);
3283 : 105 : Sxx += tmpX * tmpX * scale;
3284 : 105 : Syy += tmpY * tmpY * scale;
3285 : 105 : Sxy += tmpX * tmpY * scale;
3286 : :
3287 : : /*
3288 : : * Overflow check. We only report an overflow error when finite
3289 : : * inputs lead to infinite results. Note also that Sxx, Syy and Sxy
3290 : : * should be NaN if any of the relevant inputs are infinite, so we
3291 : : * intentionally prevent them from becoming infinite.
3292 : : */
3293 [ + - + - : 105 : if (isinf(Sx) || isinf(Sxx) || isinf(Sy) || isinf(Syy) || isinf(Sxy))
+ - + - -
+ ]
3294 : : {
2017 dean.a.rasheed@gmail 3295 [ # # # # ]:UBC 0 : if (((isinf(Sx) || isinf(Sxx)) &&
3296 [ # # # # ]: 0 : !isinf(transvalues[1]) && !isinf(newvalX)) ||
3297 [ # # # # ]: 0 : ((isinf(Sy) || isinf(Syy)) &&
3298 [ # # # # ]: 0 : !isinf(transvalues[3]) && !isinf(newvalY)) ||
3299 [ # # ]: 0 : (isinf(Sxy) &&
3300 [ # # # # ]: 0 : !isinf(transvalues[1]) && !isinf(newvalX) &&
3301 [ # # # # ]: 0 : !isinf(transvalues[3]) && !isinf(newvalY)))
1522 tgl@sss.pgh.pa.us 3302 : 0 : float_overflow_error();
3303 : :
2017 dean.a.rasheed@gmail 3304 [ # # ]: 0 : if (isinf(Sxx))
3305 : 0 : Sxx = get_float8_nan();
3306 [ # # ]: 0 : if (isinf(Syy))
3307 : 0 : Syy = get_float8_nan();
3308 [ # # ]: 0 : if (isinf(Sxy))
3309 : 0 : Sxy = get_float8_nan();
3310 : : }
3311 : : }
3312 : : else
3313 : : {
3314 : : /*
3315 : : * At the first input, we normally can leave Sxx et al as 0. However,
3316 : : * if the first input is Inf or NaN, we'd better force the dependent
3317 : : * sums to NaN; otherwise we will falsely report variance zero when
3318 : : * there are no more inputs.
3319 : : */
1401 tgl@sss.pgh.pa.us 3320 [ + + + + ]:CBC 54 : if (isnan(newvalX) || isinf(newvalX))
3321 : 12 : Sxx = Sxy = get_float8_nan();
3322 [ + - - + ]: 54 : if (isnan(newvalY) || isinf(newvalY))
1401 tgl@sss.pgh.pa.us 3323 :UBC 0 : Syy = Sxy = get_float8_nan();
3324 : : }
3325 : :
3326 : : /*
3327 : : * If we're invoked as an aggregate, we can cheat and modify our first
3328 : : * parameter in-place to reduce palloc overhead. Otherwise we construct a
3329 : : * new array with the updated transition data and return it.
3330 : : */
5179 tgl@sss.pgh.pa.us 3331 [ + + ]:CBC 159 : if (AggCheckCallContext(fcinfo, NULL))
3332 : : {
6470 3333 : 156 : transvalues[0] = N;
2017 dean.a.rasheed@gmail 3334 : 156 : transvalues[1] = Sx;
3335 : 156 : transvalues[2] = Sxx;
3336 : 156 : transvalues[3] = Sy;
3337 : 156 : transvalues[4] = Syy;
3338 : 156 : transvalues[5] = Sxy;
3339 : :
6470 tgl@sss.pgh.pa.us 3340 : 156 : PG_RETURN_ARRAYTYPE_P(transarray);
3341 : : }
3342 : : else
3343 : : {
3344 : : Datum transdatums[6];
3345 : : ArrayType *result;
3346 : :
3347 : 3 : transdatums[0] = Float8GetDatumFast(N);
2017 dean.a.rasheed@gmail 3348 : 3 : transdatums[1] = Float8GetDatumFast(Sx);
3349 : 3 : transdatums[2] = Float8GetDatumFast(Sxx);
3350 : 3 : transdatums[3] = Float8GetDatumFast(Sy);
3351 : 3 : transdatums[4] = Float8GetDatumFast(Syy);
3352 : 3 : transdatums[5] = Float8GetDatumFast(Sxy);
3353 : :
6470 tgl@sss.pgh.pa.us 3354 : 3 : result = construct_array(transdatums, 6,
3355 : : FLOAT8OID,
3356 : : sizeof(float8), FLOAT8PASSBYVAL, TYPALIGN_DOUBLE);
3357 : :
3358 : 3 : PG_RETURN_ARRAYTYPE_P(result);
3359 : : }
3360 : : }
3361 : :
3362 : : /*
3363 : : * float8_regr_combine
3364 : : *
3365 : : * An aggregate combine function used to combine two 6 fields
3366 : : * aggregate transition data into a single transition data.
3367 : : * This function is used only in two stage aggregation and
3368 : : * shouldn't be called outside aggregate context.
3369 : : */
3370 : : Datum
2928 rhaas@postgresql.org 3371 : 9 : float8_regr_combine(PG_FUNCTION_ARGS)
3372 : : {
3373 : 9 : ArrayType *transarray1 = PG_GETARG_ARRAYTYPE_P(0);
3374 : 9 : ArrayType *transarray2 = PG_GETARG_ARRAYTYPE_P(1);
3375 : : float8 *transvalues1;
3376 : : float8 *transvalues2;
3377 : : float8 N1,
3378 : : Sx1,
3379 : : Sxx1,
3380 : : Sy1,
3381 : : Syy1,
3382 : : Sxy1,
3383 : : N2,
3384 : : Sx2,
3385 : : Sxx2,
3386 : : Sy2,
3387 : : Syy2,
3388 : : Sxy2,
3389 : : tmp1,
3390 : : tmp2,
3391 : : N,
3392 : : Sx,
3393 : : Sxx,
3394 : : Sy,
3395 : : Syy,
3396 : : Sxy;
3397 : :
3398 : 9 : transvalues1 = check_float8_array(transarray1, "float8_regr_combine", 6);
3399 : 9 : transvalues2 = check_float8_array(transarray2, "float8_regr_combine", 6);
3400 : :
2017 dean.a.rasheed@gmail 3401 : 9 : N1 = transvalues1[0];
3402 : 9 : Sx1 = transvalues1[1];
3403 : 9 : Sxx1 = transvalues1[2];
3404 : 9 : Sy1 = transvalues1[3];
3405 : 9 : Syy1 = transvalues1[4];
3406 : 9 : Sxy1 = transvalues1[5];
3407 : :
3408 : 9 : N2 = transvalues2[0];
3409 : 9 : Sx2 = transvalues2[1];
3410 : 9 : Sxx2 = transvalues2[2];
3411 : 9 : Sy2 = transvalues2[3];
3412 : 9 : Syy2 = transvalues2[4];
3413 : 9 : Sxy2 = transvalues2[5];
3414 : :
3415 : : /*--------------------
3416 : : * The transition values combine using a generalization of the
3417 : : * Youngs-Cramer algorithm as follows:
3418 : : *
3419 : : * N = N1 + N2
3420 : : * Sx = Sx1 + Sx2
3421 : : * Sxx = Sxx1 + Sxx2 + N1 * N2 * (Sx1/N1 - Sx2/N2)^2 / N
3422 : : * Sy = Sy1 + Sy2
3423 : : * Syy = Syy1 + Syy2 + N1 * N2 * (Sy1/N1 - Sy2/N2)^2 / N
3424 : : * Sxy = Sxy1 + Sxy2 + N1 * N2 * (Sx1/N1 - Sx2/N2) * (Sy1/N1 - Sy2/N2) / N
3425 : : *
3426 : : * It's worth handling the special cases N1 = 0 and N2 = 0 separately
3427 : : * since those cases are trivial, and we then don't need to worry about
3428 : : * division-by-zero errors in the general case.
3429 : : *--------------------
3430 : : */
3431 [ + + ]: 9 : if (N1 == 0.0)
3432 : : {
3433 : 3 : N = N2;
3434 : 3 : Sx = Sx2;
3435 : 3 : Sxx = Sxx2;
3436 : 3 : Sy = Sy2;
3437 : 3 : Syy = Syy2;
3438 : 3 : Sxy = Sxy2;
3439 : : }
3440 [ + + ]: 6 : else if (N2 == 0.0)
3441 : : {
3442 : 3 : N = N1;
3443 : 3 : Sx = Sx1;
3444 : 3 : Sxx = Sxx1;
3445 : 3 : Sy = Sy1;
3446 : 3 : Syy = Syy1;
3447 : 3 : Sxy = Sxy1;
3448 : : }
3449 : : else
3450 : : {
3451 : 3 : N = N1 + N2;
3452 : 3 : Sx = float8_pl(Sx1, Sx2);
3453 : 3 : tmp1 = Sx1 / N1 - Sx2 / N2;
3454 : 3 : Sxx = Sxx1 + Sxx2 + N1 * N2 * tmp1 * tmp1 / N;
1522 tgl@sss.pgh.pa.us 3455 [ - + - - : 3 : if (unlikely(isinf(Sxx)) && !isinf(Sxx1) && !isinf(Sxx2))
- - ]
1522 tgl@sss.pgh.pa.us 3456 :UBC 0 : float_overflow_error();
2017 dean.a.rasheed@gmail 3457 :CBC 3 : Sy = float8_pl(Sy1, Sy2);
3458 : 3 : tmp2 = Sy1 / N1 - Sy2 / N2;
3459 : 3 : Syy = Syy1 + Syy2 + N1 * N2 * tmp2 * tmp2 / N;
1522 tgl@sss.pgh.pa.us 3460 [ - + - - : 3 : if (unlikely(isinf(Syy)) && !isinf(Syy1) && !isinf(Syy2))
- - ]
1522 tgl@sss.pgh.pa.us 3461 :UBC 0 : float_overflow_error();
2017 dean.a.rasheed@gmail 3462 :CBC 3 : Sxy = Sxy1 + Sxy2 + N1 * N2 * tmp1 * tmp2 / N;
1522 tgl@sss.pgh.pa.us 3463 [ - + - - : 3 : if (unlikely(isinf(Sxy)) && !isinf(Sxy1) && !isinf(Sxy2))
- - ]
1522 tgl@sss.pgh.pa.us 3464 :UBC 0 : float_overflow_error();
3465 : : }
3466 : :
3467 : : /*
3468 : : * If we're invoked as an aggregate, we can cheat and modify our first
3469 : : * parameter in-place to reduce palloc overhead. Otherwise we construct a
3470 : : * new array with the updated transition data and return it.
3471 : : */
2017 dean.a.rasheed@gmail 3472 [ - + ]:CBC 9 : if (AggCheckCallContext(fcinfo, NULL))
3473 : : {
2017 dean.a.rasheed@gmail 3474 :UBC 0 : transvalues1[0] = N;
3475 : 0 : transvalues1[1] = Sx;
3476 : 0 : transvalues1[2] = Sxx;
3477 : 0 : transvalues1[3] = Sy;
3478 : 0 : transvalues1[4] = Syy;
3479 : 0 : transvalues1[5] = Sxy;
3480 : :
3481 : 0 : PG_RETURN_ARRAYTYPE_P(transarray1);
3482 : : }
3483 : : else
3484 : : {
3485 : : Datum transdatums[6];
3486 : : ArrayType *result;
3487 : :
2017 dean.a.rasheed@gmail 3488 :CBC 9 : transdatums[0] = Float8GetDatumFast(N);
3489 : 9 : transdatums[1] = Float8GetDatumFast(Sx);
3490 : 9 : transdatums[2] = Float8GetDatumFast(Sxx);
3491 : 9 : transdatums[3] = Float8GetDatumFast(Sy);
3492 : 9 : transdatums[4] = Float8GetDatumFast(Syy);
3493 : 9 : transdatums[5] = Float8GetDatumFast(Sxy);
3494 : :
3495 : 9 : result = construct_array(transdatums, 6,
3496 : : FLOAT8OID,
3497 : : sizeof(float8), FLOAT8PASSBYVAL, TYPALIGN_DOUBLE);
3498 : :
3499 : 9 : PG_RETURN_ARRAYTYPE_P(result);
3500 : : }
3501 : : }
3502 : :
3503 : :
3504 : : Datum
6470 tgl@sss.pgh.pa.us 3505 : 15 : float8_regr_sxx(PG_FUNCTION_ARGS)
3506 : : {
3507 : 15 : ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0);
3508 : : float8 *transvalues;
3509 : : float8 N,
3510 : : Sxx;
3511 : :
3512 : 15 : transvalues = check_float8_array(transarray, "float8_regr_sxx", 6);
3513 : 15 : N = transvalues[0];
2017 dean.a.rasheed@gmail 3514 : 15 : Sxx = transvalues[2];
3515 : :
3516 : : /* if N is 0 we should return NULL */
6470 tgl@sss.pgh.pa.us 3517 [ - + ]: 15 : if (N < 1.0)
6470 tgl@sss.pgh.pa.us 3518 :UBC 0 : PG_RETURN_NULL();
3519 : :
3520 : : /* Note that Sxx is guaranteed to be non-negative */
3521 : :
2017 dean.a.rasheed@gmail 3522 :CBC 15 : PG_RETURN_FLOAT8(Sxx);
3523 : : }
3524 : :
3525 : : Datum
6470 tgl@sss.pgh.pa.us 3526 : 15 : float8_regr_syy(PG_FUNCTION_ARGS)
3527 : : {
3528 : 15 : ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0);
3529 : : float8 *transvalues;
3530 : : float8 N,
3531 : : Syy;
3532 : :
3533 : 15 : transvalues = check_float8_array(transarray, "float8_regr_syy", 6);
3534 : 15 : N = transvalues[0];
2017 dean.a.rasheed@gmail 3535 : 15 : Syy = transvalues[4];
3536 : :
3537 : : /* if N is 0 we should return NULL */
6470 tgl@sss.pgh.pa.us 3538 [ - + ]: 15 : if (N < 1.0)
6470 tgl@sss.pgh.pa.us 3539 :UBC 0 : PG_RETURN_NULL();
3540 : :
3541 : : /* Note that Syy is guaranteed to be non-negative */
3542 : :
2017 dean.a.rasheed@gmail 3543 :CBC 15 : PG_RETURN_FLOAT8(Syy);
3544 : : }
3545 : :
3546 : : Datum
6470 tgl@sss.pgh.pa.us 3547 : 15 : float8_regr_sxy(PG_FUNCTION_ARGS)
3548 : : {
3549 : 15 : ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0);
3550 : : float8 *transvalues;
3551 : : float8 N,
3552 : : Sxy;
3553 : :
3554 : 15 : transvalues = check_float8_array(transarray, "float8_regr_sxy", 6);
3555 : 15 : N = transvalues[0];
2017 dean.a.rasheed@gmail 3556 : 15 : Sxy = transvalues[5];
3557 : :
3558 : : /* if N is 0 we should return NULL */
6470 tgl@sss.pgh.pa.us 3559 [ - + ]: 15 : if (N < 1.0)
6470 tgl@sss.pgh.pa.us 3560 :UBC 0 : PG_RETURN_NULL();
3561 : :
3562 : : /* A negative result is valid here */
3563 : :
2017 dean.a.rasheed@gmail 3564 :CBC 15 : PG_RETURN_FLOAT8(Sxy);
3565 : : }
3566 : :
3567 : : Datum
6470 tgl@sss.pgh.pa.us 3568 : 3 : float8_regr_avgx(PG_FUNCTION_ARGS)
3569 : : {
3570 : 3 : ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0);
3571 : : float8 *transvalues;
3572 : : float8 N,
3573 : : Sx;
3574 : :
3575 : 3 : transvalues = check_float8_array(transarray, "float8_regr_avgx", 6);
3576 : 3 : N = transvalues[0];
2017 dean.a.rasheed@gmail 3577 : 3 : Sx = transvalues[1];
3578 : :
3579 : : /* if N is 0 we should return NULL */
6470 tgl@sss.pgh.pa.us 3580 [ - + ]: 3 : if (N < 1.0)
6470 tgl@sss.pgh.pa.us 3581 :UBC 0 : PG_RETURN_NULL();
3582 : :
2017 dean.a.rasheed@gmail 3583 :CBC 3 : PG_RETURN_FLOAT8(Sx / N);
3584 : : }
3585 : :
3586 : : Datum
6470 tgl@sss.pgh.pa.us 3587 : 3 : float8_regr_avgy(PG_FUNCTION_ARGS)
3588 : : {
3589 : 3 : ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0);
3590 : : float8 *transvalues;
3591 : : float8 N,
3592 : : Sy;
3593 : :
3594 : 3 : transvalues = check_float8_array(transarray, "float8_regr_avgy", 6);
3595 : 3 : N = transvalues[0];
2017 dean.a.rasheed@gmail 3596 : 3 : Sy = transvalues[3];
3597 : :
3598 : : /* if N is 0 we should return NULL */
6470 tgl@sss.pgh.pa.us 3599 [ - + ]: 3 : if (N < 1.0)
6470 tgl@sss.pgh.pa.us 3600 :UBC 0 : PG_RETURN_NULL();
3601 : :
2017 dean.a.rasheed@gmail 3602 :CBC 3 : PG_RETURN_FLOAT8(Sy / N);
3603 : : }
3604 : :
3605 : : Datum
6470 tgl@sss.pgh.pa.us 3606 : 12 : float8_covar_pop(PG_FUNCTION_ARGS)
3607 : : {
3608 : 12 : ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0);
3609 : : float8 *transvalues;
3610 : : float8 N,
3611 : : Sxy;
3612 : :
3613 : 12 : transvalues = check_float8_array(transarray, "float8_covar_pop", 6);
3614 : 12 : N = transvalues[0];
2017 dean.a.rasheed@gmail 3615 : 12 : Sxy = transvalues[5];
3616 : :
3617 : : /* if N is 0 we should return NULL */
6470 tgl@sss.pgh.pa.us 3618 [ - + ]: 12 : if (N < 1.0)
6470 tgl@sss.pgh.pa.us 3619 :UBC 0 : PG_RETURN_NULL();
3620 : :
2017 dean.a.rasheed@gmail 3621 :CBC 12 : PG_RETURN_FLOAT8(Sxy / N);
3622 : : }
3623 : :
3624 : : Datum
6470 tgl@sss.pgh.pa.us 3625 : 12 : float8_covar_samp(PG_FUNCTION_ARGS)
3626 : : {
3627 : 12 : ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0);
3628 : : float8 *transvalues;
3629 : : float8 N,
3630 : : Sxy;
3631 : :
3632 : 12 : transvalues = check_float8_array(transarray, "float8_covar_samp", 6);
3633 : 12 : N = transvalues[0];
2017 dean.a.rasheed@gmail 3634 : 12 : Sxy = transvalues[5];
3635 : :
3636 : : /* if N is <= 1 we should return NULL */
6470 tgl@sss.pgh.pa.us 3637 [ + + ]: 12 : if (N < 2.0)
3638 : 9 : PG_RETURN_NULL();
3639 : :
2017 dean.a.rasheed@gmail 3640 : 3 : PG_RETURN_FLOAT8(Sxy / (N - 1.0));
3641 : : }
3642 : :
3643 : : Datum
6470 tgl@sss.pgh.pa.us 3644 : 3 : float8_corr(PG_FUNCTION_ARGS)
3645 : : {
3646 : 3 : ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0);
3647 : : float8 *transvalues;
3648 : : float8 N,
3649 : : Sxx,
3650 : : Syy,
3651 : : Sxy;
3652 : :
3653 : 3 : transvalues = check_float8_array(transarray, "float8_corr", 6);
3654 : 3 : N = transvalues[0];
2017 dean.a.rasheed@gmail 3655 : 3 : Sxx = transvalues[2];
3656 : 3 : Syy = transvalues[4];
3657 : 3 : Sxy = transvalues[5];
3658 : :
3659 : : /* if N is 0 we should return NULL */
6470 tgl@sss.pgh.pa.us 3660 [ - + ]: 3 : if (N < 1.0)
6470 tgl@sss.pgh.pa.us 3661 :UBC 0 : PG_RETURN_NULL();
3662 : :
3663 : : /* Note that Sxx and Syy are guaranteed to be non-negative */
3664 : :
3665 : : /* per spec, return NULL for horizontal and vertical lines */
2017 dean.a.rasheed@gmail 3666 [ + - - + ]:CBC 3 : if (Sxx == 0 || Syy == 0)
6470 tgl@sss.pgh.pa.us 3667 :UBC 0 : PG_RETURN_NULL();
3668 : :
2017 dean.a.rasheed@gmail 3669 :CBC 3 : PG_RETURN_FLOAT8(Sxy / sqrt(Sxx * Syy));
3670 : : }
3671 : :
3672 : : Datum
6470 tgl@sss.pgh.pa.us 3673 : 3 : float8_regr_r2(PG_FUNCTION_ARGS)
3674 : : {
3675 : 3 : ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0);
3676 : : float8 *transvalues;
3677 : : float8 N,
3678 : : Sxx,
3679 : : Syy,
3680 : : Sxy;
3681 : :
3682 : 3 : transvalues = check_float8_array(transarray, "float8_regr_r2", 6);
3683 : 3 : N = transvalues[0];
2017 dean.a.rasheed@gmail 3684 : 3 : Sxx = transvalues[2];
3685 : 3 : Syy = transvalues[4];
3686 : 3 : Sxy = transvalues[5];
3687 : :
3688 : : /* if N is 0 we should return NULL */
6470 tgl@sss.pgh.pa.us 3689 [ - + ]: 3 : if (N < 1.0)
6470 tgl@sss.pgh.pa.us 3690 :UBC 0 : PG_RETURN_NULL();
3691 : :
3692 : : /* Note that Sxx and Syy are guaranteed to be non-negative */
3693 : :
3694 : : /* per spec, return NULL for a vertical line */
2017 dean.a.rasheed@gmail 3695 [ - + ]:CBC 3 : if (Sxx == 0)
6470 tgl@sss.pgh.pa.us 3696 :UBC 0 : PG_RETURN_NULL();
3697 : :
3698 : : /* per spec, return 1.0 for a horizontal line */
2017 dean.a.rasheed@gmail 3699 [ - + ]:CBC 3 : if (Syy == 0)
6470 tgl@sss.pgh.pa.us 3700 :UBC 0 : PG_RETURN_FLOAT8(1.0);
3701 : :
2017 dean.a.rasheed@gmail 3702 :CBC 3 : PG_RETURN_FLOAT8((Sxy * Sxy) / (Sxx * Syy));
3703 : : }
3704 : :
3705 : : Datum
6470 tgl@sss.pgh.pa.us 3706 : 3 : float8_regr_slope(PG_FUNCTION_ARGS)
3707 : : {
3708 : 3 : ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0);
3709 : : float8 *transvalues;
3710 : : float8 N,
3711 : : Sxx,
3712 : : Sxy;
3713 : :
3714 : 3 : transvalues = check_float8_array(transarray, "float8_regr_slope", 6);
3715 : 3 : N = transvalues[0];
2017 dean.a.rasheed@gmail 3716 : 3 : Sxx = transvalues[2];
3717 : 3 : Sxy = transvalues[5];
3718 : :
3719 : : /* if N is 0 we should return NULL */
6470 tgl@sss.pgh.pa.us 3720 [ - + ]: 3 : if (N < 1.0)
6470 tgl@sss.pgh.pa.us 3721 :UBC 0 : PG_RETURN_NULL();
3722 : :
3723 : : /* Note that Sxx is guaranteed to be non-negative */
3724 : :
3725 : : /* per spec, return NULL for a vertical line */
2017 dean.a.rasheed@gmail 3726 [ - + ]:CBC 3 : if (Sxx == 0)
6470 tgl@sss.pgh.pa.us 3727 :UBC 0 : PG_RETURN_NULL();
3728 : :
2017 dean.a.rasheed@gmail 3729 :CBC 3 : PG_RETURN_FLOAT8(Sxy / Sxx);
3730 : : }
3731 : :
3732 : : Datum
6470 tgl@sss.pgh.pa.us 3733 : 3 : float8_regr_intercept(PG_FUNCTION_ARGS)
3734 : : {
3735 : 3 : ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0);
3736 : : float8 *transvalues;
3737 : : float8 N,
3738 : : Sx,
3739 : : Sxx,
3740 : : Sy,
3741 : : Sxy;
3742 : :
3743 : 3 : transvalues = check_float8_array(transarray, "float8_regr_intercept", 6);
3744 : 3 : N = transvalues[0];
2017 dean.a.rasheed@gmail 3745 : 3 : Sx = transvalues[1];
3746 : 3 : Sxx = transvalues[2];
3747 : 3 : Sy = transvalues[3];
3748 : 3 : Sxy = transvalues[5];
3749 : :
3750 : : /* if N is 0 we should return NULL */
6470 tgl@sss.pgh.pa.us 3751 [ - + ]: 3 : if (N < 1.0)
6470 tgl@sss.pgh.pa.us 3752 :UBC 0 : PG_RETURN_NULL();
3753 : :
3754 : : /* Note that Sxx is guaranteed to be non-negative */
3755 : :
3756 : : /* per spec, return NULL for a vertical line */
2017 dean.a.rasheed@gmail 3757 [ - + ]:CBC 3 : if (Sxx == 0)
6470 tgl@sss.pgh.pa.us 3758 :UBC 0 : PG_RETURN_NULL();
3759 : :
2017 dean.a.rasheed@gmail 3760 :CBC 3 : PG_RETURN_FLOAT8((Sy - Sx * Sxy / Sxx) / N);
3761 : : }
3762 : :
3763 : :
3764 : : /*
3765 : : * ====================================
3766 : : * MIXED-PRECISION ARITHMETIC OPERATORS
3767 : : * ====================================
3768 : : */
3769 : :
3770 : : /*
3771 : : * float48pl - returns arg1 + arg2
3772 : : * float48mi - returns arg1 - arg2
3773 : : * float48mul - returns arg1 * arg2
3774 : : * float48div - returns arg1 / arg2
3775 : : */
3776 : : Datum
8657 tgl@sss.pgh.pa.us 3777 : 9 : float48pl(PG_FUNCTION_ARGS)
3778 : : {
3779 : 9 : float4 arg1 = PG_GETARG_FLOAT4(0);
3780 : 9 : float8 arg2 = PG_GETARG_FLOAT8(1);
3781 : :
2086 tomas.vondra@postgre 3782 : 9 : PG_RETURN_FLOAT8(float8_pl((float8) arg1, arg2));
3783 : : }
3784 : :
3785 : : Datum
8657 tgl@sss.pgh.pa.us 3786 :UBC 0 : float48mi(PG_FUNCTION_ARGS)
3787 : : {
3788 : 0 : float4 arg1 = PG_GETARG_FLOAT4(0);
3789 : 0 : float8 arg2 = PG_GETARG_FLOAT8(1);
3790 : :
2086 tomas.vondra@postgre 3791 : 0 : PG_RETURN_FLOAT8(float8_mi((float8) arg1, arg2));
3792 : : }
3793 : :
3794 : : Datum
8657 tgl@sss.pgh.pa.us 3795 : 0 : float48mul(PG_FUNCTION_ARGS)
3796 : : {
3797 : 0 : float4 arg1 = PG_GETARG_FLOAT4(0);
3798 : 0 : float8 arg2 = PG_GETARG_FLOAT8(1);
3799 : :
2086 tomas.vondra@postgre 3800 : 0 : PG_RETURN_FLOAT8(float8_mul((float8) arg1, arg2));
3801 : : }
3802 : :
3803 : : Datum
8657 tgl@sss.pgh.pa.us 3804 :CBC 3 : float48div(PG_FUNCTION_ARGS)
3805 : : {
3806 : 3 : float4 arg1 = PG_GETARG_FLOAT4(0);
3807 : 3 : float8 arg2 = PG_GETARG_FLOAT8(1);
3808 : :
2086 tomas.vondra@postgre 3809 :UBC 0 : PG_RETURN_FLOAT8(float8_div((float8) arg1, arg2));
3810 : : }
3811 : :
3812 : : /*
3813 : : * float84pl - returns arg1 + arg2
3814 : : * float84mi - returns arg1 - arg2
3815 : : * float84mul - returns arg1 * arg2
3816 : : * float84div - returns arg1 / arg2
3817 : : */
3818 : : Datum
8657 tgl@sss.pgh.pa.us 3819 :CBC 6 : float84pl(PG_FUNCTION_ARGS)
3820 : : {
3821 : 6 : float8 arg1 = PG_GETARG_FLOAT8(0);
3822 : 6 : float4 arg2 = PG_GETARG_FLOAT4(1);
3823 : :
2086 tomas.vondra@postgre 3824 : 6 : PG_RETURN_FLOAT8(float8_pl(arg1, (float8) arg2));
3825 : : }
3826 : :
3827 : : Datum
8657 tgl@sss.pgh.pa.us 3828 :UBC 0 : float84mi(PG_FUNCTION_ARGS)
3829 : : {
3830 : 0 : float8 arg1 = PG_GETARG_FLOAT8(0);
3831 : 0 : float4 arg2 = PG_GETARG_FLOAT4(1);
3832 : :
2086 tomas.vondra@postgre 3833 : 0 : PG_RETURN_FLOAT8(float8_mi(arg1, (float8) arg2));
3834 : : }
3835 : :
3836 : : Datum
8657 tgl@sss.pgh.pa.us 3837 : 0 : float84mul(PG_FUNCTION_ARGS)
3838 : : {
3839 : 0 : float8 arg1 = PG_GETARG_FLOAT8(0);
3840 : 0 : float4 arg2 = PG_GETARG_FLOAT4(1);
3841 : :
2086 tomas.vondra@postgre 3842 : 0 : PG_RETURN_FLOAT8(float8_mul(arg1, (float8) arg2));
3843 : : }
3844 : :
3845 : : Datum
8657 tgl@sss.pgh.pa.us 3846 :CBC 3 : float84div(PG_FUNCTION_ARGS)
3847 : : {
3848 : 3 : float8 arg1 = PG_GETARG_FLOAT8(0);
3849 : 3 : float4 arg2 = PG_GETARG_FLOAT4(1);
3850 : :
2086 tomas.vondra@postgre 3851 :UBC 0 : PG_RETURN_FLOAT8(float8_div(arg1, (float8) arg2));
3852 : : }
3853 : :
3854 : : /*
3855 : : * ====================
3856 : : * COMPARISON OPERATORS
3857 : : * ====================
3858 : : */
3859 : :
3860 : : /*
3861 : : * float48{eq,ne,lt,le,gt,ge} - float4/float8 comparison operations
3862 : : */
3863 : : Datum
8657 tgl@sss.pgh.pa.us 3864 :CBC 1491 : float48eq(PG_FUNCTION_ARGS)
3865 : : {
3866 : 1491 : float4 arg1 = PG_GETARG_FLOAT4(0);
3867 : 1491 : float8 arg2 = PG_GETARG_FLOAT8(1);
3868 : :
2086 tomas.vondra@postgre 3869 : 1491 : PG_RETURN_BOOL(float8_eq((float8) arg1, arg2));
3870 : : }
3871 : :
3872 : : Datum
8657 tgl@sss.pgh.pa.us 3873 : 9804 : float48ne(PG_FUNCTION_ARGS)
3874 : : {
3875 : 9804 : float4 arg1 = PG_GETARG_FLOAT4(0);
3876 : 9804 : float8 arg2 = PG_GETARG_FLOAT8(1);
3877 : :
2086 tomas.vondra@postgre 3878 : 9804 : PG_RETURN_BOOL(float8_ne((float8) arg1, arg2));
3879 : : }
3880 : :
3881 : : Datum
8657 tgl@sss.pgh.pa.us 3882 : 2134 : float48lt(PG_FUNCTION_ARGS)
3883 : : {
3884 : 2134 : float4 arg1 = PG_GETARG_FLOAT4(0);
3885 : 2134 : float8 arg2 = PG_GETARG_FLOAT8(1);
3886 : :
2086 tomas.vondra@postgre 3887 : 2134 : PG_RETURN_BOOL(float8_lt((float8) arg1, arg2));
3888 : : }
3889 : :
3890 : : Datum
8657 tgl@sss.pgh.pa.us 3891 : 12898 : float48le(PG_FUNCTION_ARGS)
3892 : : {
3893 : 12898 : float4 arg1 = PG_GETARG_FLOAT4(0);
3894 : 12898 : float8 arg2 = PG_GETARG_FLOAT8(1);
3895 : :
2086 tomas.vondra@postgre 3896 : 12898 : PG_RETURN_BOOL(float8_le((float8) arg1, arg2));
3897 : : }
3898 : :
3899 : : Datum
8657 tgl@sss.pgh.pa.us 3900 : 2246 : float48gt(PG_FUNCTION_ARGS)
3901 : : {
3902 : 2246 : float4 arg1 = PG_GETARG_FLOAT4(0);
3903 : 2246 : float8 arg2 = PG_GETARG_FLOAT8(1);
3904 : :
2086 tomas.vondra@postgre 3905 : 2246 : PG_RETURN_BOOL(float8_gt((float8) arg1, arg2));
3906 : : }
3907 : :
3908 : : Datum
8657 tgl@sss.pgh.pa.us 3909 : 2446 : float48ge(PG_FUNCTION_ARGS)
3910 : : {
3911 : 2446 : float4 arg1 = PG_GETARG_FLOAT4(0);
3912 : 2446 : float8 arg2 = PG_GETARG_FLOAT8(1);
3913 : :
2086 tomas.vondra@postgre 3914 : 2446 : PG_RETURN_BOOL(float8_ge((float8) arg1, arg2));
3915 : : }
3916 : :
3917 : : /*
3918 : : * float84{eq,ne,lt,le,gt,ge} - float8/float4 comparison operations
3919 : : */
3920 : : Datum
8657 tgl@sss.pgh.pa.us 3921 : 906 : float84eq(PG_FUNCTION_ARGS)
3922 : : {
3923 : 906 : float8 arg1 = PG_GETARG_FLOAT8(0);
3924 : 906 : float4 arg2 = PG_GETARG_FLOAT4(1);
3925 : :
2086 tomas.vondra@postgre 3926 : 906 : PG_RETURN_BOOL(float8_eq(arg1, (float8) arg2));
3927 : : }
3928 : :
3929 : : Datum
8657 tgl@sss.pgh.pa.us 3930 :UBC 0 : float84ne(PG_FUNCTION_ARGS)
3931 : : {
3932 : 0 : float8 arg1 = PG_GETARG_FLOAT8(0);
3933 : 0 : float4 arg2 = PG_GETARG_FLOAT4(1);
3934 : :
2086 tomas.vondra@postgre 3935 : 0 : PG_RETURN_BOOL(float8_ne(arg1, (float8) arg2));
3936 : : }
3937 : :
3938 : : Datum
8657 tgl@sss.pgh.pa.us 3939 :CBC 1599 : float84lt(PG_FUNCTION_ARGS)
3940 : : {
3941 : 1599 : float8 arg1 = PG_GETARG_FLOAT8(0);
3942 : 1599 : float4 arg2 = PG_GETARG_FLOAT4(1);
3943 : :
2086 tomas.vondra@postgre 3944 : 1599 : PG_RETURN_BOOL(float8_lt(arg1, (float8) arg2));
3945 : : }
3946 : :
3947 : : Datum
8657 tgl@sss.pgh.pa.us 3948 : 1899 : float84le(PG_FUNCTION_ARGS)
3949 : : {
3950 : 1899 : float8 arg1 = PG_GETARG_FLOAT8(0);
3951 : 1899 : float4 arg2 = PG_GETARG_FLOAT4(1);
3952 : :
2086 tomas.vondra@postgre 3953 : 1899 : PG_RETURN_BOOL(float8_le(arg1, (float8) arg2));
3954 : : }
3955 : :
3956 : : Datum
8657 tgl@sss.pgh.pa.us 3957 : 1599 : float84gt(PG_FUNCTION_ARGS)
3958 : : {
3959 : 1599 : float8 arg1 = PG_GETARG_FLOAT8(0);
3960 : 1599 : float4 arg2 = PG_GETARG_FLOAT4(1);
3961 : :
2086 tomas.vondra@postgre 3962 : 1599 : PG_RETURN_BOOL(float8_gt(arg1, (float8) arg2));
3963 : : }
3964 : :
3965 : : Datum
8657 tgl@sss.pgh.pa.us 3966 : 1602 : float84ge(PG_FUNCTION_ARGS)
3967 : : {
3968 : 1602 : float8 arg1 = PG_GETARG_FLOAT8(0);
3969 : 1602 : float4 arg2 = PG_GETARG_FLOAT4(1);
3970 : :
2086 tomas.vondra@postgre 3971 : 1602 : PG_RETURN_BOOL(float8_ge(arg1, (float8) arg2));
3972 : : }
3973 : :
3974 : : /*
3975 : : * Implements the float8 version of the width_bucket() function
3976 : : * defined by SQL2003. See also width_bucket_numeric().
3977 : : *
3978 : : * 'bound1' and 'bound2' are the lower and upper bounds of the
3979 : : * histogram's range, respectively. 'count' is the number of buckets
3980 : : * in the histogram. width_bucket() returns an integer indicating the
3981 : : * bucket number that 'operand' belongs to in an equiwidth histogram
3982 : : * with the specified characteristics. An operand smaller than the
3983 : : * lower bound is assigned to bucket 0. An operand greater than the
3984 : : * upper bound is assigned to an additional bucket (with number
3985 : : * count+1). We don't allow "NaN" for any of the float8 inputs, and we
3986 : : * don't allow either of the histogram bounds to be +/- infinity.
3987 : : */
3988 : : Datum
6298 neilc@samurai.com 3989 : 432 : width_bucket_float8(PG_FUNCTION_ARGS)
3990 : : {
5995 bruce@momjian.us 3991 : 432 : float8 operand = PG_GETARG_FLOAT8(0);
3992 : 432 : float8 bound1 = PG_GETARG_FLOAT8(1);
3993 : 432 : float8 bound2 = PG_GETARG_FLOAT8(2);
3994 : 432 : int32 count = PG_GETARG_INT32(3);
3995 : : int32 result;
3996 : :
381 tgl@sss.pgh.pa.us 3997 [ + + ]: 432 : if (count <= 0)
6298 neilc@samurai.com 3998 [ + - ]: 6 : ereport(ERROR,
3999 : : (errcode(ERRCODE_INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION),
4000 : : errmsg("count must be greater than zero")));
4001 : :
4002 [ + - + + : 426 : if (isnan(operand) || isnan(bound1) || isnan(bound2))
- + ]
4003 [ + - ]: 3 : ereport(ERROR,
4004 : : (errcode(ERRCODE_INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION),
4005 : : errmsg("operand, lower bound, and upper bound cannot be NaN")));
4006 : :
4007 : : /* Note that we allow "operand" to be infinite */
5534 tgl@sss.pgh.pa.us 4008 [ + + + + ]: 423 : if (isinf(bound1) || isinf(bound2))
6298 neilc@samurai.com 4009 [ + - ]: 9 : ereport(ERROR,
4010 : : (errcode(ERRCODE_INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION),
4011 : : errmsg("lower and upper bounds must be finite")));
4012 : :
4013 [ + + ]: 414 : if (bound1 < bound2)
4014 : : {
4015 [ + + ]: 297 : if (operand < bound1)
380 tgl@sss.pgh.pa.us 4016 : 57 : result = 0;
6298 neilc@samurai.com 4017 [ + + ]: 240 : else if (operand >= bound2)
4018 : : {
380 tgl@sss.pgh.pa.us 4019 [ + + ]: 60 : if (pg_add_s32_overflow(count, 1, &result))
4020 [ + - ]: 3 : ereport(ERROR,
4021 : : (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
4022 : : errmsg("integer out of range")));
4023 : : }
4024 : : else
4025 : : {
4026 [ + + + + ]: 180 : if (!isinf(bound2 - bound1))
4027 : : {
4028 : : /* The quotient is surely in [0,1], so this can't overflow */
4029 : 171 : result = count * ((operand - bound1) / (bound2 - bound1));
4030 : : }
4031 : : else
4032 : : {
4033 : : /*
4034 : : * We get here if bound2 - bound1 overflows DBL_MAX. Since
4035 : : * both bounds are finite, their difference can't exceed twice
4036 : : * DBL_MAX; so we can perform the computation without overflow
4037 : : * by dividing all the inputs by 2. That should be exact too,
4038 : : * except in the case where a very small operand underflows to
4039 : : * zero, which would have negligible impact on the result
4040 : : * given such large bounds.
4041 : : */
4042 : 9 : result = count * ((operand / 2 - bound1 / 2) / (bound2 / 2 - bound1 / 2));
4043 : : }
4044 : : /* The quotient could round to 1.0, which would be a lie */
4045 [ + + ]: 180 : if (result >= count)
4046 : 3 : result = count - 1;
4047 : : /* Having done that, we can add 1 without fear of overflow */
4048 : 180 : result++;
4049 : : }
4050 : : }
6298 neilc@samurai.com 4051 [ + + ]: 117 : else if (bound1 > bound2)
4052 : : {
4053 [ + + ]: 114 : if (operand > bound1)
380 tgl@sss.pgh.pa.us 4054 : 6 : result = 0;
6298 neilc@samurai.com 4055 [ + + ]: 108 : else if (operand <= bound2)
4056 : : {
380 tgl@sss.pgh.pa.us 4057 [ + + ]: 15 : if (pg_add_s32_overflow(count, 1, &result))
4058 [ + - ]: 3 : ereport(ERROR,
4059 : : (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
4060 : : errmsg("integer out of range")));
4061 : : }
4062 : : else
4063 : : {
4064 [ + + + + ]: 93 : if (!isinf(bound1 - bound2))
4065 : 84 : result = count * ((bound1 - operand) / (bound1 - bound2));
4066 : : else
4067 : 9 : result = count * ((bound1 / 2 - operand / 2) / (bound1 / 2 - bound2 / 2));
4068 [ + + ]: 93 : if (result >= count)
4069 : 3 : result = count - 1;
4070 : 93 : result++;
4071 : : }
4072 : : }
4073 : : else
4074 : : {
6298 neilc@samurai.com 4075 [ + - ]: 3 : ereport(ERROR,
4076 : : (errcode(ERRCODE_INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION),
4077 : : errmsg("lower bound cannot equal upper bound")));
4078 : : result = 0; /* keep the compiler quiet */
4079 : : }
4080 : :
4081 : 405 : PG_RETURN_INT32(result);
4082 : : }
|