-
-
Notifications
You must be signed in to change notification settings - Fork 35.4k
gh-155526: correct errno handling in complex_abs() #155527
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
5aa9e73
9702894
576f6cd
31d9fc3
3b851ac
c4f365e
fde8fc7
5fa214f
23d948e
28ba074
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| Correct ``errno`` handling in ``abs(complex)``. Patch by Sergey B | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "Correct errno handling" is vague. Please explain in details what behavior changes. In Python, we do no give access to errno, so it's unclear to me how users are impacted. Also, _Py_c_abs() is documented. If its behavior changes, a "versionchanged" note should be added in its documentation, and a separated Changelog (NEWS) entry should be added for _Py_c_abs() (basically repeats the abs(complex) change). |
||
| Kirpichev. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -379,32 +379,34 @@ c_powi(Py_complex x, long n) | |
| double | ||
| _Py_c_abs(Py_complex z) | ||
| { | ||
| /* sets errno = ERANGE on overflow; otherwise errno = 0 */ | ||
| /* sets errno = ERANGE on overflow */ | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No longer setting Since For example, change the API to
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unfortunately, _Py_c_abs() is documented.
I suggest: https://github.com/hpkfft/cpython/blob/erange/Objects/complexobject.c#L380-L417 Edit: I was wrong. The function
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, I forgot that
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This is not a part of the documentation and looks as a mistake. Note that other private complex C-API functions don't do this. This also corresponds to libc behavior. The C standard says:
and
I think that rare C-API users adopt above pattern, like we do in Though, I'm fine with reversion of that part if you aren't OK with arguments above.
Yes, it seems that errno-free helpers shows some speedup in simple tests (5-10%). But if we decide to change internal API functions in this way, lets do that more systematically, not just for one function. |
||
| double result; | ||
| int saved_errno = errno; | ||
|
|
||
| if (!isfinite(z.real) || !isfinite(z.imag)) { | ||
| /* C99 rules: if either the real or the imaginary part is an | ||
| infinity, return infinity, even if the other part is a | ||
| NaN. */ | ||
| if (isinf(z.real)) { | ||
| result = fabs(z.real); | ||
| errno = 0; | ||
| errno = saved_errno; | ||
| return result; | ||
| } | ||
| if (isinf(z.imag)) { | ||
| result = fabs(z.imag); | ||
| errno = 0; | ||
| errno = saved_errno; | ||
| return result; | ||
| } | ||
| /* either the real or imaginary part is a NaN, | ||
| and neither is infinite. Result should be NaN. */ | ||
| errno = saved_errno; | ||
| return Py_NAN; | ||
| } | ||
| result = hypot(z.real, z.imag); | ||
| if (!isfinite(result)) | ||
| errno = ERANGE; | ||
| else | ||
| errno = 0; | ||
| errno = saved_errno; | ||
| return result; | ||
| } | ||
|
|
||
|
|
@@ -812,7 +814,10 @@ static PyObject * | |
| complex_abs(PyObject *op) | ||
| { | ||
| PyComplexObject *v = _PyComplexObject_CAST(op); | ||
| double result = _Py_c_abs(v->cval); | ||
| double result; | ||
|
|
||
| errno = 0; | ||
| result = _Py_c_abs(v->cval); | ||
| if (errno == ERANGE) { | ||
| PyErr_SetString(PyExc_OverflowError, | ||
| "absolute value too large"); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I understand correctly, the PR changes two things in _Py_c_abs():
errnoto decide if an exception should be raised.So I would prefer to set errno to ERANGE (or another errno different than zero?), and check that errno is unchanged (is still ERANGE) once the function completes. Something like:
It seems like you have to add
_testcapi.get_errno().There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's not the case. This PR retains the documented behavior that the function
_Py_c_abs()setserrnotoERANGEon overflows.It's not clear that anyone uses this function (viz. capi-workgroup), but it seems like a good idea to retain that behavior. Somebody may be using it similarly to how Numba uses
_Py_c_pow().On success, I think it's good to leave
errnounchanged. Somebody might seterrnoto zero, and then do more than one library call with the understanding thaterrnois "sticky". The Linux man page for errno says, "the value of errno is never set to zero by any system call or library function." The philosophy, if I understand correctly, is that there may be false positives but not false negatives. That is, iferrnois set to zero, then multiple library calls, thenerrnois still zero, then all the library calls were successful.