1

I get an error when using the pow function -nan(ind) prints to the screen. Wondering if theres a way to use pow with numbers that have negetive bases and non integer exponents.

Currently the pow function is pow(-12.4112021858, 0.2). And gives me -nan(ind) error. If i change the base to just 12.41 it seems to calculate perfectly fine.

Edit - I had exponent set as a different number to 0.2

int main() {



    double a = 1.83;
    double v = 1.25;
    double r = 0;

    double sum = 0;
 
    double exponent = 0.2;

    double result = 1;
    while (true)
    {
    printf("Enter Radius \n");
    scanf_s("%lf", &r);
    sum = 1 - r/ a;
    printf("%lf\n", sum);
    sum = sum * v;
    printf("%lf\n", sum);
    sum=  pow(sum, exponent);
    printf("%lf\n", sum);

}
}
  • 1
    Probably because `0.2` is not represented exactly, so the result of `pow` is complex. – Eugene Sh. Apr 13 '21 at 18:02
  • 2
    You can't. This is a mathematical problem, not a programming problem. See https://en.wikipedia.org/wiki/Exponentiation#Real_exponents and specifically https://en.wikipedia.org/wiki/Exponentiation#Real_exponents_with_negative_bases – Bodo Apr 13 '21 at 18:11
  • 3
    you can use pow on complex numbers ... just like in math ... see simple [`cpow` implementation using `log,exp`](https://stackoverflow.com/a/56735614/2521214) – Spektre Apr 13 '21 at 18:14
  • 1
    @Spektre: `cpow(-12.4112021858, .2)` returns approximately 1.3388 + .9727 i, which I think is not what OP wants. More likely they want approximately −1.65486576. – Eric Postpischil Apr 13 '21 at 21:55
  • 1
    @EugeneSh.: `pow(-12.4112021858, .2)` produces a domain error even if .2 is represented exactly. – Eric Postpischil Apr 13 '21 at 21:56
  • @EricPostpischil You right I got a copy thypo bug in my `cln` function the atan2 parameters where negated ... now my cpow outputs the same as yours. thx for spotting (it was used only for GLSL fractal and there is hard to debug especially if the result looked the same as what it should... anyway interesting I was in hope the result will be close to rooting but looks like its not the case on complex domain (maybe the difference to rounded `1/5` in float has done that?) – Spektre Apr 14 '21 at 08:39

2 Answers2

3

Wondering if theres a way to use pow with numbers that have negetive bases and non integer exponents.

First, check the documentation. C 2018 7.12.7.4 specifies powf, pow, and powl:

The pow functions compute x raised to the power y. A domain error occurs if x is finite and negative and y is finite and not an integer value…

Your x, approximately −12.4112021858, is finite and negative, and your y, approximately .2, is finite and not an integer value. So a domain error occurs.

This means you cannot expect to get a result unless the pow you are using specifically provides support for additional cases beyond what the C standard requires, even if .2 is exactly represented in double.

(When there is a domain-error, an implementation-defined result is returned. This could be a NaN, a valid mathematical result, such as −2 for pow(-32, .2) if decimal-based floating-point is used, or something else. The implementation may also report an error via errno or a floating-point exception. See C 2018 7.12.1 for more information.)

Second, .2 is not representable in the double format of most C implementations. C implementations commonly use the IEEE-754 binary64 format. In this format, the closest representable value to .2 is 0.200000000000000011102230246251565404236316680908203125. For the source code pow(-12.4112021858, .2), the numerals are first converted to double, and then pow is called with arguments -12.41120218580000056363132898695766925811767578125 and 0.200000000000000011102230246251565404236316680908203125. So you are not requesting an operation that has a real-number result.

If your C implementation used a decimal-based floating-point, .2 would be representable, and it would be reasonable for pow(-12.4112021858, .2) to return the fifth root of x, as the fifth root is a negative real number. (This would be an extension to the standard specification of pow, as described above.)

If you know y is supposed to be one-fifth or a rational number p/q where q is odd, you can calculate the desired result as pow(fabs(x), y) if p is even and copysign(pow(fabs(x), y), x) if p is odd.

One of the comments suggesting using cpow, but that will not produce the result you want. cpow(-12.4112021858, .2) will return approximately 1.3388 + .9727 i. (The complex power “function” is multi-valued, but cpow is defined to produce that result.)

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • @EricPostpischil Looks like I solved the `cpow` problem caused by multi-valued `cln` please check my answer if I did not miss/mess something ... You much more skilled in this area then me. – Spektre Apr 14 '21 at 09:35
  • @EricPostpischil hmm I am thinking safer would be to disect the exponent to integer and decimal part and do this just for the decimal part ... you know for cases like `x^(2+1/3)` ... will test a bit more and update my answer if I found out something new – Spektre Apr 14 '21 at 11:43
0

By using complex math it is possible ... The problem is that the pow is using ln operation which is on complex domain multi-valued which means it has infinite number of valid result (as imaginary part of result is added with k*2*PI where k is any integer). So when the used result is not added with correct k then the result will be complex and not the one you want.

However for rooting (1.0/exponent -> integer) the k can be obtained directly as this:

int k=int(floor((1.0/exponent)+0.5))/2;

Which I discovered just now empirically. When I rewrite my complex math cpow to use this I got this C++ code:

//---------------------------------------------------------------------------
vec2 cadd(vec2 a,vec2 b)    // a+b
    {
    return a+b;
    }
vec2 csub(vec2 a,vec2 b)    // a-b
    {
    return a-b;
    }
vec2 cmul(vec2 a,vec2 b)    // a*b
    {
    return vec2((a.x*b.x)-(a.y*b.y),(a.x*b.y)+(a.y*b.x));
    }
vec2 cdiv(vec2 a,vec2 b)    // a/b
    {
    float an=atan2(-a.y,-a.x)-atan2(-b.y,-b.x);
    float  r=length(a)/length(b);
    return r*vec2(cos(an),sin(an));
    }
vec2 csqr(vec2 a)           // a^2
    {
    return cmul(a,a);
    }
vec2 cexp(vec2 a)           // e^a
    {
    //  e^(x+y*i)= e^x * e^(y*i) = e^x * ( cos(y) + i*sin(y) )
    return exp(a.x)*vec2(cos(a.y),sin(a.y));
    }
vec2 cln(vec2 a)            // ln(a) + i*pi2*k where k={ ...-1,0,+1,... }
    {
    return vec2(log(length(a)),atan2(a.y,a.x));
    }
vec2 cpow(vec2 a,vec2 b)    // a^b
    {
    return cexp(cmul(cln(a),b));
    }
vec2 ctet(vec2 a,int b)     // a^^b
    {
    vec2 c=vec2(1.0,0.0);
    for (;b>0;b--) c=cpow(a,c);
    return c;
    }
//-------------------------------------------------------------------------
vec2 cln(vec2 a,int k)          // ln(a) + i*pi2*k where k={ ...-1,0,+1,... }
    {
    return vec2(log(length(a)),atan2(a.y,a.x)+float(k+k)*M_PI);
    }
float mypow(float a,float b)        // a^b
    {
    if (b<0.0) return 1.0/mypow(a,-b);
    int k=0;
    if ((a<0.0)&&(b<1.0))       // rooting with negative base
        {
        k=floor((1.0/b)+0.5);
        k/=2;
        }
    return cexp(cmul(cln(vec2(a,0.0),k),vec2(b,0.0))).x;
    }
//-------------------------------------------------------------------------

I am using GLSL like math vec2 you can easily rewrite to x,y components instead for example to this:

//-------------------------------------------------------------------------
float mypow(float a,float b)        // a^b
    {
    if (b<0.0) return 1.0/mypow(a,-b);
    int k=0;
    if ((a<0.0)&&(b<1.0))           // rooting with negative base
        {
        k=floor((1.0/b)+0.5);
        k/=2;
        }
    float x,y;
    x=log(fabs(a));
    y=atan2(0.0,a)+(float(k+k)*M_PI);
    x*=b; y*=b;
//  if (fabs(exp(x)*sin(y))>1e-6) throw domain error;  // abs imaginary part is not zero
    return exp(x)*cos(y);                              // real part of result
    }
//-------------------------------------------------------------------------

Which returns real part of complex domain pow and also select the correct multi-valued cln sub-result. I also added domain test as a comment in the code in case you need/want to implement it.

Here result for your case:

mypow(-12.411202,0.200000) = -1.654866

Have tested more number and 1/odd number exponents and looks like it is working.

[Edit1] handling mixed exponents

Now if we have exponents in form a^(b0+1/b1) where b0,b1 are integers and a<0 we need to dissect the pow to 2 parts:

a^(b0+1/b1) = a^b0 * a^(1/b1)

so when added to above function:

//-------------------------------------------------------------------------
float mypow(float a,float b)            // a^b
    {
    if (b<0.0) return 1.0/mypow(a,-b);  // handle negative exponents
    int k;
    float x0,x,y,e,b0;
    k=0;                                // for normal cases k=0
    b0=floor(b);                        // integer part of exponent
    b-=b0;                              // decimal part of exponent
    // integer exponent (real domain power |a|^b0 )
    x0=pow(fabs(a),b0);
    if ((a<0.0)&&((int(b0)&1))==1) x0=-x0; // just add sign if odd exponent and negative base
    // decimal exponent (complex domain rooting a^b )
    if ((a<0.0)&&(b>0.0))               // rooting with negative base
        {
        k=floor((1.0/b)+0.5);
        k&=0xFFFFFFFE;
        }
    x=b*(log(fabs(a))); e=exp(x);
    y=b*(atan2(0.0,a)+(float(k)*M_PI));
    x=e*cos(y);
//  y=e*sin(y); if (fabs(y)>1e-6) throw domain error;
    return x*x0; // full complex result is x0*(x+i*y)
    }
//-------------------------------------------------------------------------

and sample output:

mypow(-12.41120243,3.200000) = 3163.76635742
mypow(3163.76635742,0.312500) = 12.41120243
Spektre
  • 49,595
  • 11
  • 110
  • 380
  • `floor((1.0/exponent)+0.5)` has trouble in corner cases. Consider `lround(1.0/exponent)`. – chux - Reinstate Monica Apr 14 '21 at 12:47
  • What is `length()`? Looks non-standard. – chux - Reinstate Monica Apr 14 '21 at 12:49
  • @chux-ReinstateMonica lround is undefined in math.h / C++ ... `length(vec2 a) = sqrt(a.x*a.x + a.y*a.y)` ... if you look at the standalone functions they do not relay on `vec2` nor anything else other than math.h – Spektre Apr 14 '21 at 12:50
  • `lround()` in C since C99, 20+ years. Rather than `sqrt(a.x*a.x + a.y*a.y)` consider standard [`hypot()`](http://www.cplusplus.com/reference/cmath/hypot/?kw=hypot). – chux - Reinstate Monica Apr 14 '21 at 12:52
  • @chux-ReinstateMonica length is GLSL internal function ... no need for hypot – Spektre Apr 14 '21 at 12:53
  • @chux-ReinstateMonica I use Borland/Embarcadero C++... maybe std::lround will work (I got stuff like `Round` at disposal instead but did not want to use something that is not in C) I see no point the odd result is divided by 2 anyway and if it is in edge case it will not lead to real only result no matter what `k` is used ... the round is only to account for slight rounding errors much less than `0.5` distance to nearest integer. So even if the k is off by 1 the result is still valid ... – Spektre Apr 14 '21 at 13:03