Skip to content

gh-153144: Avoid checking errno for atan2 - #153148

Open
hpkfft wants to merge 7 commits into
python:mainfrom
hpkfft:atan2
Open

gh-153144: Avoid checking errno for atan2#153148
hpkfft wants to merge 7 commits into
python:mainfrom
hpkfft:atan2

Conversation

@hpkfft

@hpkfft hpkfft commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

The C23 standard states that for atan2 and atan2pi:

A domain error may occur if both arguments are zero.
A range error occurs if x is positive and nonzero y/x is too close to zero.

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:

A range error occurs if (x is positive) and (y/x is nonzero) and (y/x is too close to zero).

@bedevere-app

bedevere-app Bot commented Jul 5, 2026

Copy link
Copy Markdown

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.

@hpkfft

hpkfft commented Jul 6, 2026

Copy link
Copy Markdown
Contributor Author

If this PR is merged, it looks to me like #146402 can (and should) be reverted.

@picnixz
picnixz requested a review from skirpichev July 6, 2026 23:41

@skirpichev skirpichev left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@hpkfft

hpkfft commented Jul 7, 2026

Copy link
Copy Markdown
Contributor Author

We must handle error in same way for all libm functions.

The mathmodule currently has 3 ways of handling 1-argument libm functions:

  • Functions that cannot overflow (e.g., acos, atan) use math_1(arg, func, /*can_overflow=*/0, err_msg)
  • Functions that can overflow (e.g., cosh, exp) use math_1(arg, func, /*can_overflow=*/1, err_msg)
  • Functions that are known to set errno properly (e.g., erf, gamma) use math_1a(arg, func, err_msg)

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).

@skirpichev

Copy link
Copy Markdown
Member

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 skirpichev left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please revert unrelated changes.

Comment thread Modules/mathmodule.c Outdated
Comment thread Modules/mathmodule.c Outdated
@hpkfft

hpkfft commented Aug 13, 2026

Copy link
Copy Markdown
Contributor Author

@vstinner, can you look at this at your convenience?
I anticipate that this PR will also fix Solaris to return the correct results (#138573), so the relevant math/cmath tests will not need to be skipped on that platform. Please advise whether I should revert #146402 as part of this PR. If so, I'll do that and would then ask you to run on the buildbots.

@vstinner vstinner left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this PR is merged, it looks to me like #146402 can (and should) be reverted.

I would prefer to update tests for Solaris in the same PR.

Comment thread Modules/cmathmodule.c
double phi;

errno = 0;
phi = atan2(z.imag, z.real); /* should not cause any exception */

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add comment: // gh-153144: ignore atan2() errno on purpose.

Comment thread Modules/mathmodule.c Outdated
if (y == -1.0 && PyErr_Occurred()) {
return NULL;
}
r = (*func)(x, y);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add comment: // ignore errno on purpose.

Comment thread Modules/mathmodule.c Outdated
"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,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add comment: // gh-153144: Ignore atan2() and atan2pi() errno on purpose.

Comment thread Modules/cmathmodule.c
errno = 0;
phi = atan2(z.imag, z.real); /* should not cause any exception */
if (errno != 0)
return math_error();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I expected a test_cmath failure when this code path is removed. Is it because glibc math library doesn't errno in this case?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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....

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants