Skip to content

Commit 7f97bde

Browse files
ViralBShahclaude
andcommitted
Set errno in tgamma() per C99/POSIX (#325)
tgamma() previously left errno unset on overflow, at the pole x == 0, and for negative-integer arguments, violating C99/POSIX and diverging from glibc. Wrap the numeric implementation (now static __tgamma) in a thin public tgamma() that classifies the (argument, result) pair and sets: - ERANGE when the result is +-Inf for a finite argument (overflow, and the pole at x == 0); - EDOM when the result is NaN for a finite argument (negative-integer arguments). In-range results and NaN/Inf inputs leave errno untouched. The internal recursive calls in neg_gam() use __tgamma directly so intermediate computations cannot perturb errno. Values returned are unchanged. Behaviour now matches system glibc for x in {0, -0.0, -2, 200, 5, subnormal}. Adds test/regression/test-325.c covering the ERANGE, EDOM, and errno-untouched cases; it fails pre-fix and passes post-fix. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent aeef4da commit 7f97bde

2 files changed

Lines changed: 85 additions & 2 deletions

File tree

bsdsrc/b_tgamma.c

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
* acknowledged.
3939
*/
4040

41+
#include <errno.h>
4142
#include <float.h>
4243
#include <openlibm_math.h>
4344

@@ -77,6 +78,7 @@
7778
* Maximum observed error < 4ulp in 1,000,000 trials.
7879
*/
7980

81+
static double __tgamma(double);
8082
static double neg_gam(double);
8183
static double small_gam(double);
8284
static double smaller_gam(double);
@@ -123,9 +125,35 @@ static struct Double ratfun_gam(double, double);
123125

124126
static const double zero = 0., one = 1.0, tiny = 1e-300;
125127

128+
/*
129+
* Public entry point. The numeric work is done by __tgamma(); this wrapper
130+
* inspects the (argument, result) pair and sets errno as required by C99/POSIX,
131+
* matching the classification used by glibc:
132+
* - pole at x == 0 (result +-Inf): ERANGE
133+
* - overflow (finite x, result +-Inf): ERANGE
134+
* - negative integer (result NaN): EDOM
135+
* Underflow to +-0 is left alone (consistent with glibc, which does not set
136+
* errno there), and normal in-range results never touch errno.
137+
*/
126138
OLM_DLLEXPORT double
127139
tgamma(x)
128140
double x;
141+
{
142+
double r = __tgamma(x);
143+
144+
if (isinf(r) && isfinite(x)) {
145+
/* Pole error at x == 0, or overflow for finite x. */
146+
errno = ERANGE;
147+
} else if (isnan(r) && isfinite(x)) {
148+
/* Domain error: negative integer argument. */
149+
errno = EDOM;
150+
}
151+
return (r);
152+
}
153+
154+
static double
155+
__tgamma(x)
156+
double x;
129157
{
130158
struct Double u;
131159

@@ -307,9 +335,9 @@ neg_gam(x)
307335
}
308336
y = one-x;
309337
if (one-y == x)
310-
y = tgamma(y);
338+
y = __tgamma(y); /* internal: do not perturb errno */
311339
else /* 1-x is inexact */
312-
y = -x*tgamma(-x);
340+
y = -x*__tgamma(-x);
313341
if (sgn < 0) y = -y;
314342
return (M_PI / (y*z));
315343
}

test/regression/test-325.c

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Regression test for issue #325: tgamma() does not set errno.
3+
* https://github.com/JuliaMath/openlibm/issues/325
4+
*
5+
* C99/POSIX require tgamma to report:
6+
* - a range error (errno == ERANGE) on overflow and at the pole x == 0,
7+
* where the result is +-HUGE_VAL;
8+
* - a domain error (errno == EDOM) for negative-integer arguments, where
9+
* the result is NaN.
10+
* In-range results must leave errno untouched.
11+
*
12+
* This pins that behaviour so the missing-errno regression cannot return.
13+
*/
14+
#include <errno.h>
15+
#include <math.h>
16+
17+
#include "regress-util.h"
18+
19+
int
20+
main(void)
21+
{
22+
double r;
23+
24+
/* Pole error at x == 0: +Inf, ERANGE. */
25+
errno = 0;
26+
r = tgamma(0.0);
27+
CHECK(isinf(r) && r > 0.0);
28+
CHECK(errno == ERANGE);
29+
30+
/* Pole error at x == -0.0: -Inf, ERANGE. */
31+
errno = 0;
32+
r = tgamma(-0.0);
33+
CHECK(isinf(r) && r < 0.0);
34+
CHECK(errno == ERANGE);
35+
36+
/* Domain error for a negative integer: NaN, EDOM. */
37+
errno = 0;
38+
r = tgamma(-2.0);
39+
CHECK(isnan(r));
40+
CHECK(errno == EDOM);
41+
42+
/* Overflow for large x: +Inf, ERANGE. */
43+
errno = 0;
44+
r = tgamma(200.0);
45+
CHECK(isinf(r) && r > 0.0);
46+
CHECK(errno == ERANGE);
47+
48+
/* Normal in-range value: exact result, errno untouched. */
49+
errno = 0;
50+
r = tgamma(5.0);
51+
CHECK(r == 24.0);
52+
CHECK(errno == 0);
53+
54+
return 0;
55+
}

0 commit comments

Comments
 (0)