dd_real atan2(const dd_real &y, const dd_real &x) { /* Strategy: Instead of using Taylor series to compute arctan, we instead use Newton's iteration to solve the equation sin(z) = y/r or cos(z) = x/r where r = sqrt(x^2 + y^2). The iteration is given by z' = z + (y - sin(z)) / cos(z) (for equation 1) z' = z - (x - cos(z)) / sin(z) (for equation 2) Here, x and y are normalized so that x^2 + y^2 = 1. If |x| > |y|, then first iteration is used since the denominator is larger. Otherwise, the second is used. */ if (x.is_zero()) { if (y.is_zero()) { /* Both x and y is zero. */ dd_real::abort("(dd_real::atan2): Both arguments zero."); return dd_real::_nan; } return (y.is_positive()) ? dd_real::_pi2 : -dd_real::_pi2; } else if (y.is_zero()) { return (x.is_positive()) ? dd_real(0.0) : dd_real::_pi; } if (x == y) { return (y.is_positive()) ? dd_real::_pi4 : -dd_real::_3pi4; } if (x == -y) { return (y.is_positive()) ? dd_real::_3pi4 : -dd_real::_pi4; } dd_real r = sqrt(sqr(x) + sqr(y)); dd_real xx = x / r; dd_real yy = y / r; /* Compute double precision approximation to atan. */ dd_real z = std::atan2(to_double(y), to_double(x)); dd_real sin_z, cos_z; if (xx > yy) { /* Use Newton iteration 1. z' = z + (y - sin(z)) / cos(z) */ sincos(z, sin_z, cos_z); z += (yy - sin_z) / cos_z; } else { /* Use Newton iteration 2. z' = z - (x - cos(z)) / sin(z) */ sincos(z, sin_z, cos_z); z -= (xx - cos_z) / sin_z; } return z; }
dd_real acos(const dd_real &a) { dd_real abs_a = abs(a); if (abs_a > 1.0) { dd_real::abort("(dd_real::acos): Argument out of domain."); return dd_real::_nan; } if (abs_a.is_one()) { return (a.is_positive()) ? dd_real(0.0) : dd_real::_pi; } return atan2(sqrt(1.0 - sqr(a)), a); }
dd_real asin(const dd_real &a) { dd_real abs_a = abs(a); if (abs_a > 1.0) { dd_real::abort("(dd_real::asin): Argument out of domain."); return dd_real::_nan; } if (abs_a.is_one()) { return (a.is_positive()) ? dd_real::_pi2 : -dd_real::_pi2; } return atan2(a, sqrt(1.0 - sqr(a))); }