Skip to content

Commit 1fe992d

Browse files
ViralBShahclaude
andcommitted
tgamma: raise FE_OVERFLOW (not FE_DIVBYZERO) on overflow (#325)
openlibm signals math errors via FP exception flags, not errno (math_errhandling == MATH_ERREXCEPT), and tgamma already raised the right flags everywhere except large-argument overflow: the x > 171.63 path returned `x / zero`, which yields +Inf but raises FE_DIVBYZERO. That flag is reserved for the pole at x == 0; a finite argument whose true gamma exceeds DBL_MAX is an overflow and must raise FE_OVERFLOW (as the function comment already documents, and as glibc does). Return `x * 0x1p1023` instead, which overflows to +Inf and raises FE_OVERFLOW. The pole (FE_DIVBYZERO) and negative-integer domain error (FE_INVALID) were already correct and are unchanged. This keeps the library consistent with its own math_errhandling rather than introducing errno, which no other openlibm function sets. Adds test/regression/test-325.c, which verifies the exception for each error class against the result value; it self-checks that the environment tracks FP flags first (qemu-user emulates them unreliably on some arches, cf. #347) and skips otherwise. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 43f291e commit 1fe992d

2 files changed

Lines changed: 73 additions & 1 deletion

File tree

bsdsrc/b_tgamma.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,14 @@ tgamma(x)
131131

132132
if (isgreaterequal(x, 6)) {
133133
if(x > 171.63)
134-
return (x / zero);
134+
/*
135+
* Overflow: the true value exceeds DBL_MAX. Return +Inf
136+
* and raise FE_OVERFLOW (per math_errhandling ==
137+
* MATH_ERREXCEPT and the function comment above). The
138+
* old `x / zero` returned +Inf but wrongly raised
139+
* FE_DIVBYZERO, which is reserved for the pole at x == 0.
140+
*/
141+
return (x * 0x1p1023);
135142
u = large_gam(x);
136143
return(__exp__D(u.a, u.b));
137144
} else if (isgreaterequal(x, 1.0 + LEFT + x0))

test/regression/test-325.c

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* Regression test for issue #325: tgamma() error signaling.
3+
* https://github.com/JuliaMath/openlibm/issues/325
4+
*
5+
* openlibm reports math errors via FP exception flags, not errno
6+
* (math_errhandling == MATH_ERREXCEPT). tgamma already raised FE_DIVBYZERO at
7+
* the pole (x == +-0) and FE_INVALID for negative-integer (domain) arguments,
8+
* but overflow for large x went through `x / zero`, which returns +Inf while
9+
* wrongly raising FE_DIVBYZERO instead of FE_OVERFLOW. This pins the correct,
10+
* glibc-matching exception for every error class.
11+
*
12+
* FP exception flags are emulated unreliably by qemu-user on some arches (see
13+
* #347), so the test first checks that the environment tracks a plain
14+
* divide-by-zero and skips if it does not.
15+
*/
16+
#include <math.h>
17+
#include <openlibm_fenv.h>
18+
19+
#include "regress-util.h"
20+
21+
static int
22+
fenv_tracks_flags(void)
23+
{
24+
volatile double z = 0.0, r;
25+
26+
feclearexcept(FE_ALL_EXCEPT);
27+
r = 1.0 / z; /* a known divide-by-zero */
28+
(void)r;
29+
return fetestexcept(FE_DIVBYZERO) != 0;
30+
}
31+
32+
static int
33+
raises(double x, int excepts)
34+
{
35+
feclearexcept(FE_ALL_EXCEPT);
36+
(void)tgamma(x);
37+
return fetestexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW) == excepts;
38+
}
39+
40+
int
41+
main(void)
42+
{
43+
if (!fenv_tracks_flags())
44+
return REGRESS_SKIP;
45+
46+
/* Pole at x == +-0: +-Inf, FE_DIVBYZERO. */
47+
CHECK(isinf(tgamma(0.0)) == 1);
48+
CHECK(raises(0.0, FE_DIVBYZERO));
49+
CHECK(isinf(tgamma(-0.0)) == -1);
50+
CHECK(raises(-0.0, FE_DIVBYZERO));
51+
52+
/* Negative integer (domain error): NaN, FE_INVALID. */
53+
CHECK(isnan(tgamma(-2.0)));
54+
CHECK(raises(-2.0, FE_INVALID));
55+
56+
/* Overflow for large finite x: +Inf, FE_OVERFLOW (was FE_DIVBYZERO). */
57+
CHECK(isinf(tgamma(200.0)) == 1);
58+
CHECK(raises(200.0, FE_OVERFLOW));
59+
60+
/* In-range result raises none of these. */
61+
CHECK(tgamma(5.0) == 24.0);
62+
CHECK(raises(5.0, 0));
63+
64+
return 0;
65+
}

0 commit comments

Comments
 (0)