finding cube root in C++?

2019-02-09 00:20发布

问题:

Strange things happen when i try to find the cube root of a number.

The following code returns me undefined. In cmd : -1.#IND

cout<<pow(( double )(20.0*(-3.2) + 30.0),( double )1/3)

While this one works perfectly fine. In cmd : 4.93242414866094

cout<<pow(( double )(20.0*4.5 + 30.0),( double )1/3)

From mathematical way it must work since we can have the cube root from a negative number. Pow is from Visual C++ 2010 math.h library. Any ideas?

回答1:

pow(x, y) from <cmath> does NOT work if x is negative and y is non-integral.

This is a limitation of std::pow, as documented in the C standard and on cppreference:

Error handling

  • Errors are reported as specified in math_errhandling
  • If base is finite and negative and exp is finite and non-integer, a domain error occurs and a range error may occur.
  • If base is zero and exp is zero, a domain error may occur.
  • If base is zero and exp is negative, a domain error or a pole error may occur.

There are a couple ways around this limitation:

  • Cube-rooting is the same as taking something to the 1/3 power, so you could do std::pow(x, 1/3.).

  • In C++11, you can use std::cbrt. C++11 introduced both square-root and cube-root functions, but no generic n-th root function that overcomes the limitations of std::pow.



回答2:

The power 1/3 is a special case. In general, non-integral powers of negative numbers are complex. It wouldn't be practical for pow to check for special cases like integer roots, and besides, 1/3 as a double is not exactly 1/3!

I don't know about the visual C++ pow, but my man page says under errors:

EDOM The argument x is negative and y is not an integral value. This would result in a complex number.

You'll have to use a more specialized cube root function if you want cube roots of negative numbers - or cut corners and take absolute value, then take cube root, then multiply the sign back on.

Note that depending on context, a negative number x to the 1/3 power is not necessarily the negative cube root you're expecting. It could just as easily be the first complex root, x^(1/3) * e^(pi*i/3). This is the convention mathematica uses; it's also reasonable to just say it's undefined.



回答3:

While (-1)^3 = -1, you can't simply take a rational power of a negative number and expect a real response. This is because there are other solutions to this rational exponent that are imaginary in nature.
http://www.wolframalpha.com/input/?i=x^(1/3),+x+from+-5+to+0

Similarily, plot x^x. For x = -1/3, this should have a solution. However, this function is deemed undefined in R for x < 0.

Therefore, don't expect math.h to do magic that would make it inefficient, just change the signs yourself.



回答4:

Guess you gotta take the negative out and put it in afterwards. You can have a wrapper do this for you if you really want to.

function yourPow(double x, double y)
{
    if (x < 0)
        return -1.0 * pow(-1.0*x, y);
    else
        return pow(x, y);
}


回答5:

Don't cast to double by using (double), use a double numeric constant instead:

double thingToCubeRoot = -20.*3.2+30;
cout<< thingToCubeRoot/fabs(thingToCubeRoot) * pow( fabs(thingToCubeRoot), 1./3. );

Should do the trick!

Also: don't include <math.h> in C++ projects, but use <cmath> instead.

Alternatively, use pow from the <complex> header for the reasons stated by buddhabrot



回答6:

pow( x, y ) is the same as (i.e. equivalent to) exp( y * log( x ) )

if log(x) is invalid then pow(x,y) is also.

Similarly you cannot perform 0 to the power of anything, although mathematically it should be 0.



回答7:

C++11 has the cbrt function (see for example http://en.cppreference.com/w/cpp/numeric/math/cbrt) so you can write something like

#include <iostream>
#include <cmath>

int main(int argc, char* argv[])
{
   const double arg = 20.0*(-3.2) + 30.0;
   std::cout << cbrt(arg) << "\n";
   std::cout << cbrt(-arg) << "\n";
   return 0;
}

I do not have access to the C++ standard so I do not know how the negative argument is handled... a test on ideone http://ideone.com/bFlXYs seems to confirm that C++ (gcc-4.8.1) extends the cube root with this rule cbrt(x)=-cbrt(-x) when x<0; for this extension you can see http://mathworld.wolfram.com/CubeRoot.html



回答8:

I was looking for cubit root and found this thread and it occurs to me that the following code might work:

#include <cmath>
using namespace std;

function double nth-root(double x, double n){
    if (!(n%2) || x<0){
        throw FAILEXCEPTION(); // even root from negative is fail
    }

    bool sign = (x >= 0);

    x = exp(log(abs(x))/n);

    return sign ? x : -x;
}


回答9:

I think you should not confuse exponentiation with the nth-root of a number. See the good old Wikipedia



回答10:

because the 1/3 will always return 0 as it will be considered as integer... try with 1.0/3.0... it is what i think but try and implement... and do not forget to declare variables containing 1.0 and 3.0 as double...



回答11:

Here's a little function I knocked up.

#define uniform() (rand()/(1.0 + RAND_MAX))

double CBRT(double Z)
{
    double guess = Z;
    double x, dx;
    int loopbreaker;

retry:
    x = guess * guess * guess;
    loopbreaker = 0;
    while (fabs(x - Z) > FLT_EPSILON)
    {
        dx = 3 * guess*guess;
        loopbreaker++;
        if (fabs(dx) < DBL_EPSILON || loopbreaker > 53)
        {
            guess += uniform() * 2 - 1.0;
            goto retry;
        }
        guess -= (x - Z) / dx;
        x = guess*guess*guess;
    }

    return guess;
}

It uses Newton-Raphson to find a cube root.

Sometime Newton -Raphson gets stuck, if the root is very close to 0 then the derivative can get large and it can oscillate. So I've clamped and forced it to restart if that happens. If you need more accuracy you can change the FLT_EPSILONs.



回答12:

If you ever have no math library you can use this way to compute the cubic root:

cubic root

double curt(double x) {
  if (x == 0) {
    // would otherwise return something like 4.257959840008151e-109
    return 0;
  }
  double b = 1; // use any value except 0
  double last_b_1 = 0;
  double last_b_2 = 0;
  while (last_b_1 != b && last_b_2 != b) {
    last_b_1 = b;
    // use (2 * b + x / b / b) / 3 for small numbers, as suggested by  willywonka_dailyblah
    b = (b + x / b / b) / 2;
    last_b_2 = b;
    // use (2 * b + x / b / b) / 3 for small numbers, as suggested by  willywonka_dailyblah
    b = (b + x / b / b) / 2;
  }
  return b;
}

It is derives from the sqrt algorithm below. The idea is that b and x / b / b bigger and smaller from the cubic root of x. So, the average of both lies closer to the cubic root of x.

Square Root And Cubic Root (in Python)

def sqrt_2(a):
    if a == 0:
        return 0
    b = 1
    last_b = 0
    while last_b != b:
        last_b = b
        b = (b + a / b) / 2
    return b

def curt_2(a):
    if a == 0:
        return 0
    b = a
    last_b_1 = 0;
    last_b_2 = 0;
    while (last_b_1 != b and last_b_2 != b):
        last_b_1 = b;
        b = (b + a / b / b) / 2;
        last_b_2 = b;
        b = (b + a / b / b) / 2;
    return b

In contrast to the square root, last_b_1 and last_b_2 are required in the cubic root because b flickers. You can modify these algorithms to compute the fourth root, fifth root and so on.

Thanks to my math teacher Herr Brenner in 11th grade who told me this algorithm for sqrt.

Performance

I tested it on an Arduino with 16mhz clock frequency:

  • 0.3525ms for yourPow
  • 0.3853ms for nth-root
  • 2.3426ms for curt


标签: c++ math.h pow