2597. std::log misspecified for complex numbers

Section: 29.5.8 [complex.transcendentals] Status: WP Submitter: Thomas Koeppe Opened: 2016-03-01 Last modified: 2017-07-30

Priority: 3

View all other issues in [complex.transcendentals].

View all issues with WP status.

Discussion:

The current specification of std::log is inconsistent for complex numbers, specifically, the Returns clause (29.5.8 [complex.transcendentals]). On the one hand, it states that the imaginary part of the return value lies in the closed interval [-i π, +i π]. On the other hand, it says that "the branch cuts are along the negative real axis" and "the imaginary part of log(x) is when x is a negative real number".

The inconsistency lies in the difference between the mathematical concept of a branch cut and the nature of floating point numbers in C++. The corresponding specification in the C standard makes it clearer that if x is a real number, then log(x + 0i) = +π, but log(x - 0i) = -π, i.e. they consider positive and negative zero to represent the two different limits of approaching the branch cut from opposite directions. In other words, the term "negative real number" is misleading, and in fact there are two distinct real numbers, x + 0i and x - 0i, that compare equal but whose logarithms differ by 2 π i.

The resolution should consist of two parts:

  1. Double-check that our usage and definition of "branch cut" is sufficiently unambiguous. The C standard contains a lot more wording around this that we don't have in C++.

  2. Change the Returns clause of log appropriately. For example: "When x is a negative real number, imag(log(x + 0i)) is π, and imag(log(x - 0i)) is ."

Current implementations seem to behave as described in (2). (Try-it-at-home link)

[2016-11-12, Issaquah]

Move to Open - Thomas to provide wording

[2016-11-15, Thomas comments and provides wording]

Following LWG discussion in Issaquah, I now propose to resolve this issue by removing the normative requirement on the function limits, and instead adding a note that the intention is to match the behaviour of C. This allows implementations to use the behaviour of C without having to specify what floating point numbers really are.

The change applies to both std::log and std::sqrt.

Updated try-at-home link, see here.

[2017-03-04, Kona]

Minor wording update and status to Tentatively Ready.

Previous resolution [SUPERSEDED]:

This wording is relative to N4606.

  1. Change the "returns" element for std::log (29.5.8 [complex.transcendentals] p17):

    template<class T> complex<T> log(const complex<T>& x);
    

    -16- Remarks: The branch cuts are along the negative real axis.

    -17- Returns: The complex natural (base-ℯ) logarithm of x. For all x, imag(log(x)) lies in the interval [-π, π], and when x is a negative real number, imag(log(x)) is π. [Note: The semantics of std::log are intended to be the same in C++ as they are for clog in C. — end note]

  2. Change the "returns" element for std::sqrt (29.5.8 [complex.transcendentals] p25):

    template<class T> complex<T> sqrt(const complex<T>& x);
    

    -24- Remarks: The branch cuts are along the negative real axis.

    -25- Returns: The complex square root of x, in the range of the right half-plane. If the argument is a negative real number, the value returned lies on the positive imaginary axis.[Note: The semantics of std::sqrt are intended to be the same in C++ as they are for csqrt in C. — end note]

Proposed resolution:

This wording is relative to N4606.

  1. Change the "returns" element for std::log (29.5.8 [complex.transcendentals] p17):

    template<class T> complex<T> log(const complex<T>& x);
    

    -16- Remarks: The branch cuts are along the negative real axis.

    -17- Returns: The complex natural (base-ℯ) logarithm of x. For all x, imag(log(x)) lies in the interval [-π, π], and when x is a negative real number, imag(log(x)) is π. [Note: the semantics of this function are intended to be the same in C++ as they are for clog in C. — end note]

  2. Change the "returns" element for std::sqrt (29.5.8 [complex.transcendentals] p25):

    template<class T> complex<T> sqrt(const complex<T>& x);
    

    -24- Remarks: The branch cuts are along the negative real axis.

    -25- Returns: The complex square root of x, in the range of the right half-plane. If the argument is a negative real number, the value returned lies on the positive imaginary axis.[Note: The semantics of this function are intended to be the same in C++ as they are for csqrt in C. — end note]