What if it were to simply be implemented as pow(v,1/3), returning exactly the same root? So at least it's consistent.

My primary goal isn't to provide mathematical robustness, it's to move std::complex closer to a drop-in replacement for numeric types.

template <typename T> complex<T> cbrt (const complex<T> &v) { return pow(v, 1.0/3.0); }

The thing is, it doesn't really matter which of the three roots it returns, the other two can always be computed. Just as swapping the sign of a square root always yields the other one (regardless of the original sign), rotating a cube root +/- 120 deg in the complex plane always yields the other two. So the rest of the roots are still easily computable if the user so desires, for example:

    using fp = double;
    using num = std::complex<fp>;

    // if we have any root    
    const num root1 = std::cbrt(...);    

    // we can compute the other two with rotations
    constexpr fp H = (fp)0.5, T = sqrt((fp)3) / (fp)2;
    const fp x = q1.real(), y = q1.imag();
    const num root2(-H*x+T*y, -T*x-H*y);
    const num root3(-H*x-T*y,  T*x-H*y);

So it really doesn't matter which of the roots it returns (just like it doesn't matter for sqrt), and if it's defined in terms of pow(), we don't have to make any new decisions.

Does that redeem the idea?


On Tue, Oct 25, 2022 at 12:05 PM Brian Bi via Std-Proposals <std-proposals@lists.isocpp.org> wrote:
The fact that there are three cube roots is precisely the problem.

If std::cbrt(z) were defined for complex z, one would expect it to yield std::exp(std::log(z)/3) (note that std::log returns the principal value). The problem with this is that if z happens to be a negative real, this will not give the same root as the std::cbrt function for reals (namely the negative real one). It will give the one that is inclined at an angle of +pi/3 from the positive x-axis.

That means there's no good way to define std::cbrt(z) for complex z: it violates either one expectation or the other.

I suspect there is no mainstream programming language that has two overloads of the cube root function---one with domain and codomain R, and one with domain and codomain C. They had to pick one---probably the real one, because the complex one you can emulate using whatever is the equivalent of the complex `pow` function. In the case of C++, the choice has been made for us already in any case.

On Tue, Oct 25, 2022 at 11:49 AM Dejan Milosavljevic via Std-Proposals <std-proposals@lists.isocpp.org> wrote:
Cubic root have three solutions.
What is the rule to uniquely pick the first one?
Rest of them we can have by multiply with cbrt( {+1,0} );

My proposal for complex cbrt:
 template< typename T/*number like*/>
  std::complex<T> cbrt(  std::complex<T>  const& c, int index =0 /*  which root to use is defined by ( index  % 3)  0,1 or 2 */  );

This might be too complex.
Any idea to make it simple?

On Tue, Oct 25, 2022 at 5:01 PM Jason C via Std-Proposals <std-proposals@lists.isocpp.org> wrote:
<complex> is conspicuously missing cbrt(). 

If I put together a proposal to add std::cbrt(std::complex) to <complex>, would there be any interest?

Any thoughts?

Std-Proposals mailing list
Std-Proposals mailing list

Brian Bi
Std-Proposals mailing list