diff options
Diffstat (limited to 'libkern/pow.c')
| -rw-r--r-- | libkern/pow.c | 68 |
1 files changed, 68 insertions, 0 deletions
diff --git a/libkern/pow.c b/libkern/pow.c new file mode 100644 index 00000000..2dd39143 --- /dev/null +++ b/libkern/pow.c @@ -0,0 +1,68 @@ +#include <lib9.h> + +double +pow(double x, double y) /* return x ^ y (exponentiation) */ +{ + double xy, y1, ye; + long i; + int ex, ey, flip; + + if(y == 0.0) + return 1.0; + + flip = 0; + if(y < 0.){ + y = -y; + flip = 1; + } + y1 = modf(y, &ye); + if(y1 != 0.0){ + if(x <= 0.) + goto zreturn; + if(y1 > 0.5) { + y1 -= 1.; + ye += 1.; + } + xy = exp(y1 * log(x)); + }else + xy = 1.0; + if(ye > 0x7FFFFFFF){ /* should be ~0UL but compiler can't convert double to ulong */ + if(x <= 0.){ + zreturn: + if(x==0. && !flip) + return 0.; + return NaN(); + } + if(flip){ + if(y == .5) + return 1./sqrt(x); + y = -y; + }else if(y == .5) + return sqrt(x); + return exp(y * log(x)); + } + x = frexp(x, &ex); + ey = 0; + i = ye; + if(i) + for(;;){ + if(i & 1){ + xy *= x; + ey += ex; + } + i >>= 1; + if(i == 0) + break; + x *= x; + ex <<= 1; + if(x < .5){ + x += x; + ex -= 1; + } + } + if(flip){ + xy = 1. / xy; + ey = -ey; + } + return ldexp(xy, ey); +} |
