From dc5a508761d7260bc863a2f3068723c298336382 Mon Sep 17 00:00:00 2001 From: Tim Peters Date: Sat, 9 Mar 2002 04:58:24 +0000 Subject: [PATCH] SF bug 525705: [2.2] underflow raise OverflowException. Another year in the quest to out-guess random C behavior. Added macros Py_ADJUST_ERANGE1(X) and Py_ADJUST_ERANGE2(X, Y). The latter is useful for functions with complex results. Two corrections to errno- after-libm-call are attempted: 1. If the platform set errno to ERANGE due to underflow, clear errno. Some unknown subset of libm versions and link options do this. It's allowed by C89, but I never figured anyone would do it. 2. If the platform did not set errno but overflow occurred, force errno to ERANGE. C89 required setting errno to ERANGE, but C99 doesn't. Some unknown subset of libm versions and link options do it the C99 way now. Bugfix candidate, but hold off until some Linux people actually try it, with and without -lieee. I'll send a help plea to Python-Dev. --- Include/pyport.h | 35 +++++++++++++++++++++++++++++++++++ Modules/cmathmodule.c | 3 +-- Objects/floatobject.c | 4 ++-- 3 files changed, 38 insertions(+), 4 deletions(-) diff --git a/Include/pyport.h b/Include/pyport.h index a831b264f9f..b311bd89b44 100644 --- a/Include/pyport.h +++ b/Include/pyport.h @@ -287,6 +287,41 @@ extern "C" { errno = ERANGE; \ } while(0) +/* Py_ADJUST_ERANGE1(x) + * Py_ADJUST_ERANGE2(x, y) + * Set errno to 0 before calling a libm function, and invoke one of these + * macros after, passing the function result(s) (Py_ADJUST_ERANGE2 is useful + * for functions returning complex results). This makes two kinds of + * adjustments to errno: (A) If it looks like the platform libm set + * errno=ERANGE due to underflow, clear errno. (B) If it looks like the + * platform libm overflowed but didn't set errno, force errno to ERANGE. In + * effect, we're trying to force a useful implementation of C89 errno + * behavior. + * Caution: + * This isn't reliable. See Py_OVERFLOWED comments. + * X and Y may be evaluated more than once. + */ +#define Py_ADJUST_ERANGE1(X) \ + do { \ + if (errno == 0) { \ + if ((X) == Py_HUGE_VAL || (X) == -Py_HUGE_VAL) \ + errno = ERANGE; \ + } \ + else if (errno == ERANGE && (X) == 0.0) \ + errno = 0; \ + } while(0) + +#define Py_ADJUST_ERANGE2(X, Y) \ + do { \ + if ((X) == Py_HUGE_VAL || (X) == -Py_HUGE_VAL || \ + (Y) == Py_HUGE_VAL || (Y) == -Py_HUGE_VAL) { \ + if (errno == 0) \ + errno = ERANGE; \ + } \ + else if (errno == ERANGE) \ + errno = 0; \ + } while(0) + /************************************************************************** Prototypes that are missing from the standard include files on some systems (and possibly only some versions of such systems.) diff --git a/Modules/cmathmodule.c b/Modules/cmathmodule.c index adf76b8a331..6e796808436 100644 --- a/Modules/cmathmodule.c +++ b/Modules/cmathmodule.c @@ -337,8 +337,7 @@ math_1(PyObject *args, Py_complex (*func)(Py_complex)) PyFPE_START_PROTECT("complex function", return 0) x = (*func)(x); PyFPE_END_PROTECT(x) - Py_SET_ERANGE_IF_OVERFLOW(x.real); - Py_SET_ERANGE_IF_OVERFLOW(x.imag); + Py_ADJUST_ERANGE2(x.real, x.imag); if (errno != 0) return math_error(); else diff --git a/Objects/floatobject.c b/Objects/floatobject.c index ec8f71940b2..83987baa6b2 100644 --- a/Objects/floatobject.c +++ b/Objects/floatobject.c @@ -577,9 +577,9 @@ float_pow(PyObject *v, PyObject *w, PyObject *z) PyFPE_START_PROTECT("pow", return NULL) ix = pow(iv, iw); PyFPE_END_PROTECT(ix) - Py_SET_ERANGE_IF_OVERFLOW(ix); + Py_ADJUST_ERANGE1(ix); if (errno != 0) { - /* XXX could it be another type of error? */ + assert(errno == ERANGE); PyErr_SetFromErrno(PyExc_OverflowError); return NULL; }