Skip to content

Commit a3b6dfd

Browse files
committed
Add support for gamma() and lgamma() functions.
These are useful general-purpose math functions which are included in POSIX and C99, and are commonly included in other math libraries, so expose them as SQL-callable functions. Author: Dean Rasheed <dean.a.rasheed@gmail.com> Reviewed-by: Stepan Neretin <sncfmgg@gmail.com> Reviewed-by: Peter Eisentraut <peter@eisentraut.org> Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us> Reviewed-by: Dmitry Koval <d.koval@postgrespro.ru> Reviewed-by: Alexandra Wang <alexandra.wang.oss@gmail.com> Discussion: https://postgr.es/m/CAEZATCXpGyfjXCirFk9au+FvM0y2Ah+2-0WSJx7MO368ysNUPA@mail.gmail.com
1 parent 7c82b4f commit a3b6dfd

File tree

6 files changed

+214
-1
lines changed

6 files changed

+214
-1
lines changed

doc/src/sgml/func.sgml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1396,6 +1396,27 @@ SELECT NOT(ROW(table.*) IS NOT NULL) FROM TABLE; -- detect at least one null in
13961396
</para></entry>
13971397
</row>
13981398

1399+
<row>
1400+
<entry role="func_table_entry"><para role="func_signature">
1401+
<indexterm>
1402+
<primary>gamma</primary>
1403+
</indexterm>
1404+
<function>gamma</function> ( <type>double precision</type> )
1405+
<returnvalue>double precision</returnvalue>
1406+
</para>
1407+
<para>
1408+
Gamma function
1409+
</para>
1410+
<para>
1411+
<literal>gamma(0.5)</literal>
1412+
<returnvalue>1.772453850905516</returnvalue>
1413+
</para>
1414+
<para>
1415+
<literal>gamma(6)</literal>
1416+
<returnvalue>120</returnvalue>
1417+
</para></entry>
1418+
</row>
1419+
13991420
<row>
14001421
<entry role="func_table_entry"><para role="func_signature">
14011422
<indexterm>
@@ -1436,6 +1457,23 @@ SELECT NOT(ROW(table.*) IS NOT NULL) FROM TABLE; -- detect at least one null in
14361457
</para></entry>
14371458
</row>
14381459

1460+
<row>
1461+
<entry role="func_table_entry"><para role="func_signature">
1462+
<indexterm>
1463+
<primary>lgamma</primary>
1464+
</indexterm>
1465+
<function>lgamma</function> ( <type>double precision</type> )
1466+
<returnvalue>double precision</returnvalue>
1467+
</para>
1468+
<para>
1469+
Natural logarithm of the absolute value of the gamma function
1470+
</para>
1471+
<para>
1472+
<literal>lgamma(1000)</literal>
1473+
<returnvalue>5905.220423209181</returnvalue>
1474+
</para></entry>
1475+
</row>
1476+
14391477
<row>
14401478
<entry role="func_table_entry"><para role="func_signature">
14411479
<indexterm>

src/backend/utils/adt/float.c

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2786,6 +2786,94 @@ derfc(PG_FUNCTION_ARGS)
27862786
}
27872787

27882788

2789+
/* ========== GAMMA FUNCTIONS ========== */
2790+
2791+
2792+
/*
2793+
* dgamma - returns the gamma function of arg1
2794+
*/
2795+
Datum
2796+
dgamma(PG_FUNCTION_ARGS)
2797+
{
2798+
float8 arg1 = PG_GETARG_FLOAT8(0);
2799+
float8 result;
2800+
2801+
/*
2802+
* Handle NaN and Inf cases explicitly. This simplifies the overflow
2803+
* checks on platforms that do not set errno.
2804+
*/
2805+
if (isnan(arg1))
2806+
result = arg1;
2807+
else if (isinf(arg1))
2808+
{
2809+
/* Per POSIX, an input of -Inf causes a domain error */
2810+
if (arg1 < 0)
2811+
{
2812+
float_overflow_error();
2813+
result = get_float8_nan(); /* keep compiler quiet */
2814+
}
2815+
else
2816+
result = arg1;
2817+
}
2818+
else
2819+
{
2820+
/*
2821+
* Note: the POSIX/C99 gamma function is called "tgamma", not "gamma".
2822+
*
2823+
* On some platforms, tgamma() will not set errno but just return Inf,
2824+
* NaN, or zero to report overflow/underflow; therefore, test those
2825+
* cases explicitly (note that, like the exponential function, the
2826+
* gamma function has no zeros).
2827+
*/
2828+
errno = 0;
2829+
result = tgamma(arg1);
2830+
2831+
if (errno != 0 || isinf(result) || isnan(result))
2832+
{
2833+
if (result != 0.0)
2834+
float_overflow_error();
2835+
else
2836+
float_underflow_error();
2837+
}
2838+
else if (result == 0.0)
2839+
float_underflow_error();
2840+
}
2841+
2842+
PG_RETURN_FLOAT8(result);
2843+
}
2844+
2845+
2846+
/*
2847+
* dlgamma - natural logarithm of absolute value of gamma of arg1
2848+
*/
2849+
Datum
2850+
dlgamma(PG_FUNCTION_ARGS)
2851+
{
2852+
float8 arg1 = PG_GETARG_FLOAT8(0);
2853+
float8 result;
2854+
2855+
/*
2856+
* Note: lgamma may not be thread-safe because it may write to a global
2857+
* variable signgam, which may not be thread-local. However, this doesn't
2858+
* matter to us, since we don't use signgam.
2859+
*/
2860+
errno = 0;
2861+
result = lgamma(arg1);
2862+
2863+
/*
2864+
* If an ERANGE error occurs, it means there was an overflow or a pole
2865+
* error (which happens for zero and negative integer inputs).
2866+
*
2867+
* On some platforms, lgamma() will not set errno but just return infinity
2868+
* to report overflow, but it should never underflow.
2869+
*/
2870+
if (errno == ERANGE || (isinf(result) && !isinf(arg1)))
2871+
float_overflow_error();
2872+
2873+
PG_RETURN_FLOAT8(result);
2874+
}
2875+
2876+
27892877

27902878
/*
27912879
* =========================

src/include/catalog/catversion.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,6 @@
5757
*/
5858

5959
/* yyyymmddN */
60-
#define CATALOG_VERSION_NO 202503251
60+
#define CATALOG_VERSION_NO 202503261
6161

6262
#endif

src/include/catalog/pg_proc.dat

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3564,6 +3564,13 @@
35643564
proname => 'erfc', prorettype => 'float8', proargtypes => 'float8',
35653565
prosrc => 'derfc' },
35663566

3567+
{ oid => '8702', descr => 'gamma function',
3568+
proname => 'gamma', prorettype => 'float8', proargtypes => 'float8',
3569+
prosrc => 'dgamma' },
3570+
{ oid => '8703', descr => 'natural logarithm of absolute value of gamma function',
3571+
proname => 'lgamma', prorettype => 'float8', proargtypes => 'float8',
3572+
prosrc => 'dlgamma' },
3573+
35673574
{ oid => '1618',
35683575
proname => 'interval_mul', prorettype => 'interval',
35693576
proargtypes => 'interval float8', prosrc => 'interval_mul' },

src/test/regress/expected/float8.out

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -829,6 +829,63 @@ FROM (VALUES (float8 '-infinity'),
829829
NaN | NaN | NaN
830830
(22 rows)
831831

832+
RESET extra_float_digits;
833+
-- gamma functions
834+
-- we run these with extra_float_digits = -1, to get consistently rounded
835+
-- results on all platforms.
836+
SET extra_float_digits = -1;
837+
SELECT x,
838+
gamma(x),
839+
lgamma(x)
840+
FROM (VALUES (0.5), (1), (2), (3), (4), (5),
841+
(float8 'infinity'), (float8 'nan')) AS t(x);
842+
x | gamma | lgamma
843+
----------+-----------------+------------------
844+
0.5 | 1.7724538509055 | 0.5723649429247
845+
1 | 1 | 0
846+
2 | 1 | 0
847+
3 | 2 | 0.69314718055995
848+
4 | 6 | 1.7917594692281
849+
5 | 24 | 3.1780538303479
850+
Infinity | Infinity | Infinity
851+
NaN | NaN | NaN
852+
(8 rows)
853+
854+
-- test overflow/underflow handling
855+
SELECT gamma(float8 '-infinity');
856+
ERROR: value out of range: overflow
857+
SELECT lgamma(float8 '-infinity');
858+
lgamma
859+
----------
860+
Infinity
861+
(1 row)
862+
863+
SELECT gamma(float8 '-1000.5');
864+
ERROR: value out of range: underflow
865+
SELECT lgamma(float8 '-1000.5');
866+
lgamma
867+
------------------
868+
-5914.4377011169
869+
(1 row)
870+
871+
SELECT gamma(float8 '-1');
872+
ERROR: value out of range: overflow
873+
SELECT lgamma(float8 '-1');
874+
ERROR: value out of range: overflow
875+
SELECT gamma(float8 '0');
876+
ERROR: value out of range: overflow
877+
SELECT lgamma(float8 '0');
878+
ERROR: value out of range: overflow
879+
SELECT gamma(float8 '1000');
880+
ERROR: value out of range: overflow
881+
SELECT lgamma(float8 '1000');
882+
lgamma
883+
-----------------
884+
5905.2204232092
885+
(1 row)
886+
887+
SELECT lgamma(float8 '1e308');
888+
ERROR: value out of range: overflow
832889
RESET extra_float_digits;
833890
-- test for over- and underflow
834891
INSERT INTO FLOAT8_TBL(f1) VALUES ('10e400');

src/test/regress/sql/float8.sql

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,29 @@ FROM (VALUES (float8 '-infinity'),
245245

246246
RESET extra_float_digits;
247247

248+
-- gamma functions
249+
-- we run these with extra_float_digits = -1, to get consistently rounded
250+
-- results on all platforms.
251+
SET extra_float_digits = -1;
252+
SELECT x,
253+
gamma(x),
254+
lgamma(x)
255+
FROM (VALUES (0.5), (1), (2), (3), (4), (5),
256+
(float8 'infinity'), (float8 'nan')) AS t(x);
257+
-- test overflow/underflow handling
258+
SELECT gamma(float8 '-infinity');
259+
SELECT lgamma(float8 '-infinity');
260+
SELECT gamma(float8 '-1000.5');
261+
SELECT lgamma(float8 '-1000.5');
262+
SELECT gamma(float8 '-1');
263+
SELECT lgamma(float8 '-1');
264+
SELECT gamma(float8 '0');
265+
SELECT lgamma(float8 '0');
266+
SELECT gamma(float8 '1000');
267+
SELECT lgamma(float8 '1000');
268+
SELECT lgamma(float8 '1e308');
269+
RESET extra_float_digits;
270+
248271
-- test for over- and underflow
249272
INSERT INTO FLOAT8_TBL(f1) VALUES ('10e400');
250273

0 commit comments

Comments
 (0)