-
-
Notifications
You must be signed in to change notification settings - Fork 30.4k
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
cmath module documentation is misleading on branch cuts #85417
Comments
The documentation for the cmath module is misleading on the behaviour near branch cuts. For example, the documentation for cmath.acos says: Return the arc cosine of x. There are two branch cuts: One That "continuous from below" and "continuous from above" language is misleading; in fact what happens on the vast majority of systems (those for which the floating-point format used is IEEE 754 binary64), if the imaginary part of x is zero, the sign of x is used to determine which side of the branch cut x lies. |
Correction: That should say "the sign of the imaginary part of x is used [...]" |
+1 for changing the language to match the actual mechanics.
I'm trying to see where this happens. Is this part of cmath_sqrt? if (z.real >= 0.) {
r.real = s;
r.imag = copysign(d, z.imag);
} else {
r.real = d;
r.imag = copysign(s, z.imag);
}
I'm curious, is that language incorrect? My mental image of a branch cut is a helical graph with the edge cases being continuous from above and below. Likewise, my mental model for branch cut logic is it resolves multiple possible output values in a way preserves continuity from one side or the other. In other words, I think about branch cuts in terms of continuity rather than sign preservation. Is that incorrect? |
Yes, that looks like the right part of the sqrt code. For the acos docstring, "continuous from below" implies that for any complex number z that lies exactly _on_ the branch cut, acos(z) is close to acos(w) for a nearby value w just _below_ the branch cut. But that's demonstrably not true: see the change of sign in the real part below: >>> acos(complex(2.3, -1e-10)) # value just "below" the branch cut
(4.828045495852677e-11+1.475044781241425j)
>>> acos(complex(2.3, 0.0)) # nearby value exactly _on_ the branch cut
-1.475044781241425j In effect, for a branch cut along the real axis, the sign of the zero in the imaginary part of the argument allows us to be continuous from both sides at once. |
Grr. Stupid fingers. That should say "imaginary part", not "real part" |
@mdickinson, it seems that some MP libraries use different convention, e.g.: >>> from mpmath import acos
>>> acos(2 + 1e-10j)
mpc(real='5.773502691896258e-11', imag='-1.3169578969248168')
>>> acos(complex(2))
mpc(real='0.0', imag='1.3169578969248168')
>>> acos(2 - 1e-10j)
mpc(real='5.773502691896258e-11', imag='1.3169578969248168')
>>> acos(-2 + 1e-10j)
mpc(real='3.1415926535320584', imag='-1.3169578969248168')
>>> acos(complex(-2))
mpc(real='3.1415926535897931', imag='-1.3169578969248168')
>>> acos(-2 - 1e-10j)
mpc(real='3.1415926535320584', imag='1.3169578969248168') Ditto Mathematica. That's exactly "continuous from below ... and ... continuous from above." from the cmath docs. Shouldn't we adjust the implementation instead? |
Definitely not! The implementation was very deliberately written this way. It's based on Kahan's "Branch Cuts for Complex Elementary Functions" paper, and it agrees with C99's Annex G and many other languages. |
Ok, I see. Looks a bit surprising for complex analysis, but I see why it does make sense with signed zero floating-point arithmetics. But it seems, that the current docs contains (since 2008) a note ("On platforms with hardware and system-level support for signed zeros, functions involving branch cuts are continuous on both sides of the branch cut: the sign of the zero distinguishes one side of the branch cut from the other. On platforms that do not support signed zeros the continuity is as specified below."). With this remark in mind - the acos docs seems correct, isn't? Edit: So far, after 5ea052b - IEEE 754 support is a strict requirement. IIUIC, we can just leave the above remark (or like C11's Annex G "The functions are continuous onto both sides of their branch cuts, taking into account the sign of |
Indeed - I wrote that note. :-)
Technically correct, yes, but I think unhelpful in its current form: it relies on someone reading the documentation also reading that note, rather than just reading the direct description of the relevant function. It's been almost 15 years since I wrote that note, and in those 15 years IEEE 754 has only increased its overwhelming dominance. The descriptions should describe what happens on the 100% of systems whose C double has signed zeros, rather than describing the behaviour on the 0% of systems where the C double doesn't support signed zeros. We could then update the note to note what happens on systems where the double doesn't support signed zeros, or we could just throw it out altogether. I'd probably go for the latter - the non-signed-zero systems are pretty much entirely hypothetical at this point. |
…orms without signed zero IIUIC, after 5ea052b - IEEE 754 support is a strict requirement.
This PR updates the cmath module documentation to reflect the reality that Python is almost always (and as far as I can tell, that "almost" can be omitted) running on a machine whose C double supports signed zeros. * Removes misleading references to functions being continuous from above / below / the left / the right at branch cuts * Expands the note on branch cuts at the top of the module documentation to explain the double-sided sign-of-zero-based behaviour
…hon#102046) This PR updates the cmath module documentation to reflect the reality that Python is almost always (and as far as I can tell, that "almost" can be omitted) running on a machine whose C double supports signed zeros. * Removes misleading references to functions being continuous from above / below / the left / the right at branch cuts * Expands the note on branch cuts at the top of the module documentation to explain the double-sided sign-of-zero-based behaviour
* main: (60 commits) pythongh-102056: Fix a few bugs in error handling of exception printing code (python#102078) pythongh-102011: use sys.exception() instead of sys.exc_info() in docs where possible (python#102012) pythongh-101566: Sync with zipp 3.14. (pythonGH-102018) pythonGH-99818: improve the documentation for zipfile.Path and Traversable (pythonGH-101589) pythongh-88233: zipfile: handle extras after a zip64 extra (pythonGH-96161) pythongh-101981: Apply HOMEBREW related environment variables (pythongh-102074) pythongh-101907: Stop using `_Py_OPCODE` and `_Py_OPARG` macros (pythonGH-101912) pythongh-101819: Adapt _io types to heap types, batch 1 (pythonGH-101949) pythongh-101981: Build macOS as recommended by the devguide (pythonGH-102070) pythongh-97786: Fix compiler warnings in pytime.c (python#101826) pythongh-101578: Amend PyErr_{Set,Get}RaisedException docs (python#101962) Misc improvements to the float tutorial (pythonGH-102052) pythongh-85417: Clarify behaviour on branch cuts in cmath module (python#102046) pythongh-100425: Update tutorial docs related to sum() accuracy (FH-101854) Add missing 'is' to `cmath.log()` docstring (python#102049) pythongh-100210: Correct the comment link for unescaping HTML (python#100212) pythongh-97930: Also include subdirectory in makefile. (python#102030) pythongh-99735: Use required=True in argparse subparsers example (python#100927) Fix incorrectly documented attribute in csv docs (python#101250) pythonGH-84783: Make the slice object hashable (pythonGH-101264) ...
* main: (225 commits) pythongh-102056: Fix a few bugs in error handling of exception printing code (python#102078) pythongh-102011: use sys.exception() instead of sys.exc_info() in docs where possible (python#102012) pythongh-101566: Sync with zipp 3.14. (pythonGH-102018) pythonGH-99818: improve the documentation for zipfile.Path and Traversable (pythonGH-101589) pythongh-88233: zipfile: handle extras after a zip64 extra (pythonGH-96161) pythongh-101981: Apply HOMEBREW related environment variables (pythongh-102074) pythongh-101907: Stop using `_Py_OPCODE` and `_Py_OPARG` macros (pythonGH-101912) pythongh-101819: Adapt _io types to heap types, batch 1 (pythonGH-101949) pythongh-101981: Build macOS as recommended by the devguide (pythonGH-102070) pythongh-97786: Fix compiler warnings in pytime.c (python#101826) pythongh-101578: Amend PyErr_{Set,Get}RaisedException docs (python#101962) Misc improvements to the float tutorial (pythonGH-102052) pythongh-85417: Clarify behaviour on branch cuts in cmath module (python#102046) pythongh-100425: Update tutorial docs related to sum() accuracy (FH-101854) Add missing 'is' to `cmath.log()` docstring (python#102049) pythongh-100210: Correct the comment link for unescaping HTML (python#100212) pythongh-97930: Also include subdirectory in makefile. (python#102030) pythongh-99735: Use required=True in argparse subparsers example (python#100927) Fix incorrectly documented attribute in csv docs (python#101250) pythonGH-84783: Make the slice object hashable (pythonGH-101264) ...
…honGH-102046) This PR updates the cmath module documentation to reflect the reality that Python is almost always (and as far as I can tell, that "almost" can be omitted) running on a machine whose C double supports signed zeros. * Removes misleading references to functions being continuous from above / below / the left / the right at branch cuts * Expands the note on branch cuts at the top of the module documentation to explain the double-sided sign-of-zero-based behaviour (cherry picked from commit b513c46) Co-authored-by: Mark Dickinson <dickinsm@gmail.com>
…honGH-102046) This PR updates the cmath module documentation to reflect the reality that Python is almost always (and as far as I can tell, that "almost" can be omitted) running on a machine whose C double supports signed zeros. * Removes misleading references to functions being continuous from above / below / the left / the right at branch cuts * Expands the note on branch cuts at the top of the module documentation to explain the double-sided sign-of-zero-based behaviour (cherry picked from commit b513c46) Co-authored-by: Mark Dickinson <dickinsm@gmail.com>
…-102046) (#102275) gh-85417: Clarify behaviour on branch cuts in cmath module (GH-102046) This PR updates the cmath module documentation to reflect the reality that Python is almost always (and as far as I can tell, that "almost" can be omitted) running on a machine whose C double supports signed zeros. * Removes misleading references to functions being continuous from above / below / the left / the right at branch cuts * Expands the note on branch cuts at the top of the module documentation to explain the double-sided sign-of-zero-based behaviour (cherry picked from commit b513c46) Co-authored-by: Mark Dickinson <dickinsm@gmail.com>
…-102046) (#102276) gh-85417: Clarify behaviour on branch cuts in cmath module (GH-102046) This PR updates the cmath module documentation to reflect the reality that Python is almost always (and as far as I can tell, that "almost" can be omitted) running on a machine whose C double supports signed zeros. * Removes misleading references to functions being continuous from above / below / the left / the right at branch cuts * Expands the note on branch cuts at the top of the module documentation to explain the double-sided sign-of-zero-based behaviour (cherry picked from commit b513c46) Co-authored-by: Mark Dickinson <dickinsm@gmail.com>
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
Linked PRs
The text was updated successfully, but these errors were encountered: