| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Sorry, something went wrong.
|
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 skip news label instead. |
Sorry, something went wrong.
|
If this PR is merged, it looks to me like #146402 can (and should) be reverted. |
Sorry, something went wrong.
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.
Sorry, something went wrong.
The mathmodule currently has 3 ways of handling 1-argument libm functions:
Maybe it's not so bad to have math_2ne for 2-argument functions that are known not to need error checking (e.g., atan2) as well as the current math_2 (for the others). |
Sorry, something went wrong.
|
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. |
Sorry, something went wrong.
There was a problem hiding this comment.
Please revert unrelated changes.
Sorry, something went wrong.
|
@vstinner, can you look at this at your convenience? |
Sorry, something went wrong.
|
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). |
Sorry, something went wrong.
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.) |
Sorry, something went wrong.
Then we loose chance of using new helper function for copysign().
!buildbot Solaris |
Sorry, something went wrong.
|
!buildbot Solaris |
Sorry, something went wrong.
|
🤖 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: Solaris The builders matched are:
|
Sorry, something went wrong.
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. |
Sorry, something went wrong.
|
@serhiy-storchaka @skirpichev @picnixz: What is your opinion on changing cmath.phase() behavior? |
Sorry, something went wrong.
|
FWIW, I don't think there is a change in behavior: atan2() shouldn't overflow. Underflows in Python are silent. |
Sorry, something went wrong.
|
I totally forgot about this PR. Let me read the issue and the history first (and now my speech resembles that of an AI...) |
Sorry, something went wrong.
Well, fixing a bug is a change in behavior. But I know what you mean. Let me walk through an example (using Python 3.13.5 built to use GNU libm). >>> z1 = complex(+1E300, 1E-320) >>> z2 = complex(-1E300, 1E-320) and note that, for both of them, imag/real underflows to zero: >>> 1E-320 / +1E300 0.0 >>> 1E-320 / -1E300 -0.0 The math module's atan2 gives the correct result for the following: >>> math.atan2(1E-320, +1E300) 0.0 >>> math.atan2(1E-320, -1E300) 3.141592653589793 In the first case, the result underflows to 0.0. The atan2() function in GNU's libm sets errno = ERANGE to indicate underflow. That is, the infinitely precise result is not exactly zero, but libm rounded the result to zero in order to return a double precision result. Python code in mathmodule.c ignores errno == ERANGE on underflow. DetailsThe wrapper math_2() calls is_error(), which does the following: else if (errno == ERANGE) {
if (fabs(x) < 1.5) // <==== x is the atan2 result, which is 0.0
result = 0;
else
PyErr_SetString(PyExc_OverflowError,
"math range error");
}
In the second case, the infinitely precise result is very close to Pi, and libm rounds the result to three point something in order to return a double precision result. The result did not underflow to zero (nor did it overflow to infinity), so GNU's libm leaves errno unchanged. Python code had set errno = 0 before calling libm's atan2(), so it's still zero. Python returns the result. Now, we get to the bug. >>> cmath.phase(z1)
Traceback (most recent call last):
File "<python-input-12>", line 1, in <module>
cmath.phase(z1)
~~~~~~~~~~~^^^^
OverflowError: math range error
Python calls GNU libm's atan2() exactly as before. The result is 0.0 and errno == ERANGE. Python sees that (errno != 0) and raises OverflowError. There's no code in cmathmodule.c to distinguish underflow from overflow. In the second case, >>> cmath.phase(z2) 3.141592653589793 GNU's atan2() returns the correct result and leaves errno unchanged, so it's still zero. Python returns the result. With this PR: >>> z1 = complex(+1E300, 1E-320) >>> z2 = complex(-1E300, 1E-320) >>> math.atan2(1E-320, +1E300) 0.0 >>> math.atan2(1E-320, -1E300) 3.141592653589793 >>> cmath.phase(z1) 0.0 >>> cmath.phase(z2) 3.141592653589793 |
Sorry, something went wrong.
|
@picnixz: So did you find time to review this change? |
Sorry, something went wrong.
|
No I'm sorry. I am quite busy in my daily job and haven't found time for that. I think I will have time on Sunday as I'm flying (or next thursday as I'm flying back). But the past weeks have been very busy (I can only do simple triaging or review). Now, after a quick glance:
This makes at least cmath.phase(complex(1e300, 1e-320)) and math.atan2(1e-230, 1e300) consistent. However, I would like to make sure that you document the reason why we ignore errno in this case (that is, atan2() can only raise an optional EDOM, which we can ignore, or an errno due to overflow/underflow. Since overflow is not possible for atan2, we only have underflows and since underflows are silent in Python, we don't care. Did I catch the issue well? we wouldn't break code though, just fixing things. What I'm worried about however is implementations that really want to raise an EDOM for $(\pm0, \pm0)$. According to Wikipedia:
In addition, Annex F says what to return when using (0,0). I would have imagined:
But in the second case, we can say it's garbage in-garbage out and it's likely an unsupported compiler (even less supported than icx!) So... I guess we canj ust ignore EDOM? Did I get it right? |
Sorry, something went wrong.
Done.
Yes. Although, technically, it's about the C math library, not the compiler. (It's possible to mix and match, e.g., use the Intel or Solaris math libraries with GCC.) Perhaps the title of my bug report for icx was too informal. Note that a math library might not strictly conform to Annex F for many reasons. Here, we only care about the values it returns for atan2(), which I would venture to guess would be correct even if the compiler and math library as a whole cannot claim to be compliant with the entirety of Annex F. Finally, I would like to suggest that, in my opinion, raising an exception where one should not happen is a serious bug. A Python programmer would not be expected to put cmath.phase() inside a try block. Therefore, this bug in Python leads to a crash. My understanding is that bugfixes for Python 3.13 must be merged this month. |
Sorry, something went wrong.
|
Do not click the "Update branch" button without a good reason because it notifies everyone watching the PR that there are new changes, when there are not, and it uses up limited CI resources. |
Sorry, something went wrong.
|
We unfortunately get past 3.13 release and it is not security-only. I am ok with putting it in 3.16 and maybe 3.14/3.15 but I would be interested in having some NumPy's opinion just in case (cc @ngoldbaum). I think fixing the phase/atan2 inconsistency takes precedence over other behavioral change concerns. |
Sorry, something went wrong.
|
Thanks for the ping. NumPy already behaves the way CPython will behave after this PR. The only difference is that NumPy can optionally surface the underflow, and it reports it as an underflow, never as an overflow. |
Sorry, something went wrong.
|
Yeah so this matches what users will expect. I am fine with this PR then! thanks for doing it. |
Sorry, something went wrong.
There was a problem hiding this comment.
LGTM.
@skirpichev PTAL again
Sorry, something went wrong.
| /* variant of math_2, to be used when the function being wrapped is known NOT | ||
| to need error checking (i.e., no overflow, invalid, or divide-by-zero). */ |
There was a problem hiding this comment.
I suggest expand this with atan2() example and remove other comments below.
Sorry, something went wrong.
| if (y == -1.0 && PyErr_Occurred()) { | ||
| return NULL; | ||
| } | ||
| r = (*func)(x, y); // Ignore errno on purpose. |
There was a problem hiding this comment.
| r = (*func)(x, y); // Ignore errno on purpose. | |
| r = (*func)(x, y); |
Sorry, something went wrong.
There was a problem hiding this comment.
@vstinner asked for this comment on August 14: #153148 (comment)
Sorry, something went wrong.
| /* gh-153144: Ignore atan2 and atan2pi errno on purpose since it can optionally | ||
| * be EDOM, which we should ignore, or ERANGE on underflow, which is | ||
| * silent on Python. Overflow is not possible. */ |
There was a problem hiding this comment.
| /* gh-153144: Ignore atan2 and atan2pi errno on purpose since it can optionally | |
| * be EDOM, which we should ignore, or ERANGE on underflow, which is | |
| * silent on Python. Overflow is not possible. */ |
Sorry, something went wrong.
There was a problem hiding this comment.
@vstinner asked for the "ignore errno on purpose" part of this comment on August 14: #153148 (comment)
@picnixz asked to "document the reason why we ignore errno in this case" on September 18: #153148 (comment)
Sorry, something went wrong.
| C89 but for which HUGE_VAL is not an infinity. | ||
|
|
||
| For most two-argument functions (copysign, fmod, hypot, atan2) | ||
| For most two-argument functions (fmod, hypot) |
There was a problem hiding this comment.
Actually, the only remaining math_2()-type function is remainder()...
Sorry, something went wrong.
There was a problem hiding this comment.
I think (fmod, hypot) in the comment above refers to the C math library functions.
Unless I've missed it, Python does not use the C math library function double remainder(double, double), so I don't think we should add it to this comment.
Sorry, something went wrong.
There was a problem hiding this comment.
I think (fmod, hypot) in the comment above refers to the C math library functions.
I don't think so. Before 53876d9, both fmod and hypot were using FUNC2 macro.
Python does not use the C math library function double remainder(double, double)
Yes, but m_remainder (name in style m_log and co, suggesting a workarounded libm's function) is just an implementation of Annex F remainder.
Perhaps, nowadays we could replace this function by standard library call: at least this works well for me locally. See previous attempt of removing legacy stuff: #101679
Edit: FYI, #158826.
Sorry, something went wrong.
There was a problem hiding this comment.
Interesting. But it seems sufficiently unrelated as to allow this PR to be merged without further tweaks to the comments.
Sorry, something went wrong.
| Back | FazBrowse Home | New Git URL |
The C23 standard states that for atan2 and atan2pi:
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 errno when calling these trig functions. As a bonus, math.atan2() is about 4% faster.
The statement about range error in the standard should, I think, be interpreted as: