/** * Two arguments arctangent function * @param y ordinate * @param x abscissa * @return phase angle of point (x,y) between {@code -PI} and {@code PI} */ public static double Arc(double y, double x) { if (y == 0) { double result = x * y; double invx = 1d / x; double invy = 1d / y; if (invx == 0) { // X is infinite if (x > 0) { return(y); // return +/- 0.0 } else { return(BitOps.CopySign(Math.PI, y)); } } if (x < 0 || invx < 0) { if (y < 0 || invy < 0) { return(-Math.PI); } else { return(Math.PI); } } else { return(result); } } // y cannot now be zero if (y == Double.PositiveInfinity) { if (x == Double.PositiveInfinity) { return(Math.PI * F_1_4); } if (x == Double.PositiveInfinity) { return(Math.PI * F_3_4); } return(Math.PI * F_1_2); } if (y == Double.NegativeInfinity) { if (x == Double.PositiveInfinity) { return(-Math.PI * F_1_4); } if (x == Double.NegativeInfinity) { return(-Math.PI * F_3_4); } return(-Math.PI * F_1_2); } if (x == Double.PositiveInfinity) { if (y > 0 || 1 / y > 0) { return(0d); } if (y < 0 || 1 / y < 0) { return(-0d); } } if (x == Double.NegativeInfinity) { if (y > 0.0 || 1 / y > 0.0) { return(Math.PI); } if (y < 0 || 1 / y < 0) { return(-Math.PI); } } // Neither y nor x can be infinite or NAN here if (x == 0) { if (y > 0 || 1 / y > 0) { return(Math.PI * F_1_2); } if (y < 0 || 1 / y < 0) { return(-Math.PI * F_1_2); } } // Compute ratio r = y/x double r = y / x; if (Double.IsInfinity(r)) { // bypass calculations that can create NaN return(Arc(r, 0, x < 0)); } double ra = BitOps.HighPart(r); double rb = r - ra; // Split x double xa = BitOps.HighPart(x); double xb = x - xa; rb += (y - ra * xa - ra * xb - rb * xa - rb * xb) / x; double temp = ra + rb; rb = -(temp - ra - rb); ra = temp; if (ra == 0) { // Fix up the sign so atan works correctly ra = BitOps.CopySign(0d, y); } // Call atan return(Arc(ra, rb, x < 0)); }
/** Internal helper function to compute arctangent. * @param xa number from which arctangent is requested * @param xb extra bits for x (may be 0.0) * @param leftPlane if true, result angle must be put in the left half plane * @return atan(xa + xb) (or angle shifted by {@code PI} if leftPlane is true) */ private static double Arc(double xa, double xb, bool leftPlane) { bool negate = false; int idx; if (xa == 0.0) { // Matches +/- 0.0; return correct sign return(leftPlane ? BitOps.CopySign(Math.PI, xa) : xa); } if (xa < 0) { // negative xa = -xa; xb = -xb; negate = true; } if (xa > 1.633123935319537E16) { // Very large input return((negate ^ leftPlane) ? (-Math.PI * F_1_2) : (Math.PI * F_1_2)); } /* Estimate the closest tabulated arctan value, compute eps = xa-tangentTable */ if (xa < 1) { idx = (int)(((-1.7168146928204136 * xa * xa + 8.0) * xa) + 0.5); } else { double oneOverXa = 1 / xa; idx = (int)(-((-1.7168146928204136 * oneOverXa * oneOverXa + 8.0) * oneOverXa) + 13.07); } double epsA = xa - TANGENT_TABLE_A[idx]; double epsB = -(epsA - xa + TANGENT_TABLE_A[idx]); epsB += xb - TANGENT_TABLE_B[idx]; double temp = epsA + epsB; epsB = -(temp - epsA - epsB); epsA = temp; /* Compute eps = eps / (1.0 + xa*tangent) */ temp = xa * BitOps.HEX_40000000; double ya = xa + temp - temp; double yb = xb + xa - ya; xa = ya; xb += yb; //if (idx > 8 || idx == 0) if (idx == 0) { /* If the slope of the arctan is gentle enough (< 0.45), this approximation will suffice */ //double denom = 1.0 / (1.0 + xa*tangentTableA[idx] + xb*tangentTableA[idx] + xa*tangentTableB[idx] + xb*tangentTableB[idx]); double denom = 1d / (1d + (xa + xb) * (TANGENT_TABLE_A[idx] + TANGENT_TABLE_B[idx])); //double denom = 1.0 / (1.0 + xa*tangentTableA[idx]); ya = epsA * denom; yb = epsB * denom; } else { double temp2 = xa * TANGENT_TABLE_A[idx]; double za = 1d + temp2; double zb = -(za - 1d - temp2); temp2 = xb * TANGENT_TABLE_A[idx] + xa * TANGENT_TABLE_B[idx]; temp = za + temp2; zb += -(temp - za - temp2); za = temp; zb += xb * TANGENT_TABLE_B[idx]; ya = epsA / za; temp = ya * BitOps.HEX_40000000; double yaa = (ya + temp) - temp; double yab = ya - yaa; temp = za * BitOps.HEX_40000000; double zaa = (za + temp) - temp; double zab = za - zaa; /* Correct for rounding in division */ yb = (epsA - yaa * zaa - yaa * zab - yab * zaa - yab * zab) / za; yb += -epsA * zb / za / za; yb += epsB / za; } epsA = ya; epsB = yb; /* Evaluate polynomial */ double epsA2 = epsA * epsA; /* * yb = -0.09001346640161823; * yb = yb * epsA2 + 0.11110718400605211; * yb = yb * epsA2 + -0.1428571349122913; * yb = yb * epsA2 + 0.19999999999273194; * yb = yb * epsA2 + -0.33333333333333093; * yb = yb * epsA2 * epsA; */ yb = 0.07490822288864472; yb = yb * epsA2 + -0.09088450866185192; yb = yb * epsA2 + 0.11111095942313305; yb = yb * epsA2 + -0.1428571423679182; yb = yb * epsA2 + 0.19999999999923582; yb = yb * epsA2 + -0.33333333333333287; yb = yb * epsA2 * epsA; ya = epsA; temp = ya + yb; yb = -(temp - ya - yb); ya = temp; /* Add in effect of epsB. atan'(x) = 1/(1+x^2) */ yb += epsB / (1d + epsA * epsA); //result = yb + eighths[idx] + ya; double za = BitOps.EIGHTHS[idx] + ya; double zb = -(za - BitOps.EIGHTHS[idx] - ya); temp = za + yb; zb += -(temp - za - yb); za = temp; double result = za + zb; double resultb = -(result - za - zb); if (leftPlane) { // Result is in the left plane double pia = 1.5707963267948966 * 2; double pib = 6.123233995736766E-17 * 2; za = pia - result; zb = -(za - pia + result); zb += pib - resultb; result = za + zb; resultb = -(result - za - zb); } if (negate ^ leftPlane) { result = -result; } return(result); }