gh-153144: Avoid checking errno for atan2 - #153148
Conversation
|
Most changes to Python require a NEWS entry. Add one using the blurb_it web app or the blurb command-line tool. If this change has little impact on Python users, wait for a maintainer to apply the |
|
If this PR is merged, it looks to me like #146402 can (and should) be reverted. |
skirpichev
left a comment
There was a problem hiding this comment.
No, I doubt that this is a right approach. We must handle error in same way for all libm functions.
Perhaps, m_atan2() (like m_log1p() we have currently) could be restored (see #122681) to workaround broken platform functions.
The mathmodule currently has 3 ways of handling 1-argument libm functions:
Maybe it's not so bad to have |
|
Perhaps, we could modify math_2() helper to check that errno!=EDOM, if inputs and output are finite. When domain error occurs - result should be nan. But I think that a little wrapper for libm's atan2 is better, if we are going to add some workaround for the given issue. |
skirpichev
left a comment
There was a problem hiding this comment.
Please revert unrelated changes.
|
@vstinner, can you look at this at your convenience? |
| double phi; | ||
|
|
||
| errno = 0; | ||
| phi = atan2(z.imag, z.real); /* should not cause any exception */ |
There was a problem hiding this comment.
Please add comment: // gh-153144: ignore atan2() errno on purpose.
| if (y == -1.0 && PyErr_Occurred()) { | ||
| return NULL; | ||
| } | ||
| r = (*func)(x, y); |
There was a problem hiding this comment.
Please add comment: // ignore errno on purpose.
| "Return the arc tangent (measured in radians) of x.\n\n" | ||
| "The result is between -pi/2 and pi/2.") | ||
| FUNC2(atan2, atan2, | ||
| FUNC2NE(atan2, atan2, |
There was a problem hiding this comment.
Please add comment: // gh-153144: Ignore atan2() and atan2pi() errno on purpose.
| errno = 0; | ||
| phi = atan2(z.imag, z.real); /* should not cause any exception */ | ||
| if (errno != 0) | ||
| return math_error(); |
There was a problem hiding this comment.
I expected a test_cmath failure when this code path is removed. Is it because glibc math library doesn't errno in this case?
There was a problem hiding this comment.
Yes. With the exception of the Intel and Solaris math libraries, errno is not set in this case by any math library that Python cares about. I say this because test_phase in Lib/test/test_cmath.py has asserted that return values are correct since Python 3.14, and nobody has complained. If errno were set by the C math library, the unittest would fail with ValueError: math domain error.
🌱 Some math libraries (e.g., musl) don't set errno for anything, so Python cannot rely on errno for detecting overflow or invalid. I would think that errno checking can be removed everywhere....
|
You also could use new helper function for copysign. On another hand, I would prefer just remove errno stuff from math_2(), per #156145. It should be possible for all two-argument functions: atan2, atan2pi, copysign, remainder and fmod (which could utilize same wrapper). |
| return math_error(); | ||
| else | ||
| return PyFloat_FromDouble(phi); | ||
| /* gh-153144: Ignore atan2() errno on purpose. */ |
There was a problem hiding this comment.
IMO, it's rather redundant after inline comment above.
There was a problem hiding this comment.
Victor asked for all these comments.
| "Return the arc tangent (measured in radians) of x.\n\n" | ||
| "The result is between -pi/2 and pi/2.") | ||
| FUNC2(atan2, atan2, | ||
| FUNC2NE(atan2, atan2, // gh-153144: Ignore atan2() errno on purpose. |
There was a problem hiding this comment.
| FUNC2NE(atan2, atan2, // gh-153144: Ignore atan2() errno on purpose. | |
| FUNC2NE(atan2, atan2, |
| "Return the arc tangent (measured in radians) of y/x.\n\n" | ||
| "Unlike atan(y/x), the signs of both x and y are considered.") | ||
| FUNC2(atan2pi, m_atan2pi, | ||
| FUNC2NE(atan2pi, m_atan2pi, // gh-153144: Ignore atan2pi() errno on purpose. |
There was a problem hiding this comment.
| FUNC2NE(atan2pi, m_atan2pi, // gh-153144: Ignore atan2pi() errno on purpose. | |
| FUNC2NE(atan2pi, m_atan2pi, |
| if (y == -1.0 && PyErr_Occurred()) { | ||
| return NULL; | ||
| } | ||
| r = (*func)(x, y); // Ignore errno on purpose. |
There was a problem hiding this comment.
Perhaps, expand the comment for function?
My thinking is only to do what is necessary to fix the bug (the incorrect results when building with Intel or Solaris math libraries). Then, I would recommend applying this fix to Python 3.14 and 3.15. With that in mind, I don't want to make any enhancements that are not necessary to fix the reported bug.
Yes, 156145 can have a more ambitious goal. Can you trigger the buildbots to run this PR? I am not able to check myself that it works on Solaris. (I do think it will work based on what I read in the Solaris bug report.) |
Then we loose chance of using new helper function for copysign().
!buildbot Solaris |
|
!buildbot Solaris |
|
🤖 New build scheduled with the buildbot fleet by @skirpichev for commit 5b6d005 🤖 Results will be shown at: https://buildbot.python.org/all/#/grid?branch=refs%2Fpull%2F153148%2Fmerge The command will test the builders whose names match following regular expression: The builders matched are:
|
OK, I'll add that commit tomorrow. Since it will be a separate commit, it can easily be reverted (or skipped when squashing all the commits before the final merge). The same is true for the comments Victor requested--they're in their own separate commit. |
The C23 standard states that for
atan2andatan2pi:Since Python should not raise ValueError in either of these cases (i.e., when both arguments are zero or when the computation underflows), this PR avoids checking
errnowhen calling these trig functions. As a bonus,math.atan2()is about 4% faster.math.atan2(0.0, 0.0)andcmath.phase(0.0)using icx #153144The statement about range error in the standard should, I think, be interpreted as: