这是Java
StrictMath
实现中的
pow
方法。从技术上讲,
StrictMath
更偏向于精度而非性能,但实际上它非常快,因为它在Java中被广泛使用。我已经为您复制和格式化了
pow
函数所需的源代码 :) :
public class YourMathClass {
public static double pow(double x, double y) {
if (y == 0)
return 1;
if (y == 1)
return x;
if (y == -1)
return 1 / x;
if (x != x || y != y)
return Double.NaN;
int yisint = 0;
if (x < 0 && floor(y) == y)
yisint = (y % 2 == 0) ? 2 : 1;
double ax = abs(x);
double ay = abs(y);
if (ay == Double.POSITIVE_INFINITY) {
if (ax == 1)
return Double.NaN;
if (ax > 1)
return y > 0 ? y : 0;
return y < 0 ? -y : 0;
}
if (y == 2)
return x * x;
if (y == 0.5)
return sqrt(x);
if (x == 0 || ax == Double.POSITIVE_INFINITY || ax == 1) {
if (y < 0)
ax = 1 / ax;
if (x < 0) {
if (x == -1 && yisint == 0)
ax = Double.NaN;
else if (yisint == 1)
ax = -ax;
}
return ax;
}
if (x < 0 && yisint == 0)
return Double.NaN;
double t;
double t1;
double t2;
double u;
double v;
double w;
if (ay > TWO_31) {
if (ay > TWO_64)
return ((ax < 1) ? y < 0 : y > 0) ? Double.POSITIVE_INFINITY : 0;
if (ax < 0.9999995231628418)
return y < 0 ? Double.POSITIVE_INFINITY : 0;
if (ax >= 1.0000009536743164)
return y > 0 ? Double.POSITIVE_INFINITY : 0;
t = x - 1;
w = t * t * (0.5 - t * (1 / 3.0 - t * 0.25));
u = INV_LN2_H * t;
v = t * INV_LN2_L - w * INV_LN2;
t1 = (float) (u + v);
t2 = v - (t1 - u);
} else {
long bits = Double.doubleToLongBits(ax);
int exp = (int) (bits >> 52);
if (exp == 0)
{
ax *= TWO_54;
bits = Double.doubleToLongBits(ax);
exp = (int) (bits >> 52) - 54;
}
exp -= 1023;
ax = Double.longBitsToDouble((bits & 0x000fffffffffffffL) | 0x3ff0000000000000L);
boolean k;
if (ax < SQRT_1_5)
k = false;
else if (ax < SQRT_3)
k = true;
else {
k = false;
ax *= 0.5;
exp++;
}
u = ax - (k ? 1.5 : 1);
v = 1 / (ax + (k ? 1.5 : 1));
double s = u * v;
double s_h = (float) s;
double t_h = (float) (ax + (k ? 1.5 : 1));
double t_l = ax - (t_h - (k ? 1.5 : 1));
double s_l = v * ((u - s_h * t_h) - s_h * t_l);
double s2 = s * s;
double r = s_l * (s_h + s) + s2 * s2 * (L1 + s2 * (L2 + s2 * (L3 + s2 * (L4 + s2 * (L5 + s2 * L6)))));
s2 = s_h * s_h;
t_h = (float) (3.0 + s2 + r);
t_l = r - (t_h - 3.0 - s2);
u = s_h * t_h;
v = s_l * t_h + t_l * s;
double p_h = (float) (u + v);
double p_l = v - (p_h - u);
double z_h = CP_H * p_h;
double z_l = CP_L * p_h + p_l * CP + (k ? DP_L : 0);
t = exp;
t1 = (float) (z_h + z_l + (k ? DP_H : 0) + t);
t2 = z_l - (t1 - t - (k ? DP_H : 0) - z_h);
}
boolean negative = x < 0 && yisint == 1;
double y1 = (float) y;
double p_l = (y - y1) * t1 + y * t2;
double p_h = y1 * t1;
double z = p_l + p_h;
if (z >= 1024)
{
if (z > 1024 || p_l + OVT > z - p_h)
return negative ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY;
} else if (z <= -1075)
{
if (z < -1075 || p_l <= z - p_h)
return negative ? -0.0 : 0;
}
int n = round((float) z);
p_h -= n;
t = (float) (p_l + p_h);
u = t * LN2_H;
v = (p_l - (t - p_h)) * LN2 + t * LN2_L;
z = u + v;
w = v - (z - u);
t = z * z;
t1 = z - t * (P1 + t * (P2 + t * (P3 + t * (P4 + t * P5))));
double r = (z * t1) / (t1 - 2) - (w + z * w);
z = scale(1 - (r - z), n);
return negative ? -z : z;
}
public static int round(float f) {
return (int) floor(f + 0.5f);
}
public static double floor(double a) {
double x = abs(a);
if (!(x < TWO_52) || (long) a == a)
return a;
if (x < 1)
return a >= 0 ? 0 * a : -1;
return a < 0 ? (long) a - 1.0 : (long) a;
}
public static double abs(double d) {
return (d <= 0) ? 0 - d : d;
}
public static double sqrt(double x) {
if (x < 0)
return Double.NaN;
if (x == 0 || !(x < Double.POSITIVE_INFINITY))
return x;
long bits = Double.doubleToLongBits(x);
int exp = (int) (bits >> 52);
if (exp == 0)
{
x *= TWO_54;
bits = Double.doubleToLongBits(x);
exp = (int) (bits >> 52) - 54;
}
exp -= 1023;
bits = (bits & 0x000fffffffffffffL) | 0x0010000000000000L;
if ((exp & 1) == 1)
bits <<= 1;
exp >>= 1;
bits <<= 1;
long q = 0;
long s = 0;
long r = 0x0020000000000000L;
while (r != 0) {
long t = s + r;
if (t <= bits) {
s = t + r;
bits -= t;
q += r;
}
bits <<= 1;
r >>= 1;
}
if (bits != 0)
q += q & 1;
return Double.longBitsToDouble((q >> 1) + ((exp + 1022L) << 52));
}
private static double scale(double x, int n) {
if (x == 0 || x == Double.NEGATIVE_INFINITY || !(x < Double.POSITIVE_INFINITY) || n == 0)
return x;
long bits = Double.doubleToLongBits(x);
int exp = (int) (bits >> 52) & 0x7ff;
if (exp == 0)
{
x *= TWO_54;
exp = ((int) (Double.doubleToLongBits(x) >> 52) & 0x7ff) - 54;
}
exp += n;
if (exp > 0x7fe)
return Double.POSITIVE_INFINITY * x;
if (exp > 0)
return Double.longBitsToDouble((bits & 0x800fffffffffffffL) | ((long) exp << 52));
if (exp <= -54)
return 0 * x;
exp += 54;
x = Double.longBitsToDouble((bits & 0x800fffffffffffffL) | ((long) exp << 52));
return x * (1 / TWO_54);
}
private static final double TWO_31 = 0x80000000L,
TWO_52 = 0x10000000000000L,
TWO_54 = 0x40000000000000L,
TWO_64 = 1.8446744073709552e19;
private static final double L1 = 0.5999999999999946,
L2 = 0.4285714285785502,
L3 = 0.33333332981837743,
L4 = 0.272728123808534,
L5 = 0.23066074577556175,
L6 = 0.20697501780033842,
P1 = 0.16666666666666602,
P2 = -2.7777777777015593e-3,
P3 = 6.613756321437934e-5,
P4 = -1.6533902205465252e-6,
P5 = 4.1381367970572385e-8,
DP_H = 0.5849624872207642,
DP_L = 1.350039202129749e-8,
OVT = 8.008566259537294e-17;
private static final double SQRT_1_5 = 1.224744871391589,
SQRT_3 = 1.7320508075688772,
CP = 0.9617966939259756,
CP_H = 0.9617967009544373,
CP_L = -7.028461650952758e-9,
LN2 = 0.6931471805599453,
LN2_H = 0.6931471803691238,
LN2_L = 1.9082149292705877e-10,
INV_LN2 = 1.4426950408889634,
INV_LN2_H = 1.4426950216293335,
INV_LN2_L = 1.9259629911266175e-8;
}
现在,如果你真的想让大规模计算变得非常快(我指的是数十万次pow
方法调用),我建议你使用pow
函数,它来自Apache Commons Math FastMath
类,这个函数非常快。
根据你的问题,你不想要另一个库,所以我把FastMath
和FastMathLiteralArrays
的必要部分复制到了一个文件中。这个文件太大了,无法放在StackOverflow上,所以我把它放在了PasteAll上:
http://www.pasteall.org/53238/java
警告:这个文件的代码非常庞大。除非你确定要使用pow
进行极长时间的计算,否则不应该使用此文件,因为1)它将使用大量内存,2)如果你只是使用它一两次,它可能会变得更慢。
无论如何,希望这回答了你所有关于pow
效率的需求 :) (我也希望我能得到奖励;))
pow
或者exp
和log
函数。 - Henry