dd_real cos(const dd_real &a) { if (a.is_zero()) { return 1.0; } // approximately reduce modulo 2*pi dd_real z = nint(a / dd_real::_2pi); dd_real r = a - z * dd_real::_2pi; // approximately reduce modulo pi/2 and then modulo pi/16 dd_real t; double q = std::floor(r.x[0] / dd_real::_pi2.x[0] + 0.5); t = r - dd_real::_pi2 * q; int j = static_cast<int>(q); q = std::floor(t.x[0] / _pi16.x[0] + 0.5); t -= _pi16 * q; int k = static_cast<int>(q); int abs_k = std::abs(k); if (j < -2 || j > 2) { dd_real::error("(dd_real::cos): Cannot reduce modulo pi/2."); return dd_real::_nan; } if (abs_k > 4) { dd_real::error("(dd_real::cos): Cannot reduce modulo pi/16."); return dd_real::_nan; } if (k == 0) { switch (j) { case 0: return cos_taylor(t); case 1: return -sin_taylor(t); case -1: return sin_taylor(t); default: return -cos_taylor(t); } } dd_real sin_t, cos_t; sincos_taylor(t, sin_t, cos_t); dd_real u = cos_table[abs_k-1]; dd_real v = sin_table[abs_k-1]; if (j == 0) { if (k > 0) { r = u * cos_t - v * sin_t; } else { r = u * cos_t + v * sin_t; } } else if (j == 1) { if (k > 0) { r = - u * sin_t - v * cos_t; } else { r = v * cos_t - u * sin_t; } } else if (j == -1) { if (k > 0) { r = u * sin_t + v * cos_t; } else { r = u * sin_t - v * cos_t; } } else { if (k > 0) { r = v * sin_t - u * cos_t; } else { r = - u * cos_t - v * sin_t; } } return r; }
void sincos(const dd_real &a, dd_real &sin_a, dd_real &cos_a) { if (a.is_zero()) { sin_a = 0.0; cos_a = 1.0; return; } // approximately reduce modulo 2*pi dd_real z = nint(a / dd_real::_2pi); dd_real r = a - dd_real::_2pi * z; // approximately reduce module pi/2 and pi/16 dd_real t; double q = std::floor(r.x[0] / dd_real::_pi2.x[0] + 0.5); t = r - dd_real::_pi2 * q; int j = static_cast<int>(q); int abs_j = std::abs(j); q = std::floor(t.x[0] / _pi16.x[0] + 0.5); t -= _pi16 * q; int k = static_cast<int>(q); int abs_k = std::abs(k); if (abs_j > 2) { dd_real::error("(dd_real::sincos): Cannot reduce modulo pi/2."); cos_a = sin_a = dd_real::_nan; return; } if (abs_k > 4) { dd_real::error("(dd_real::sincos): Cannot reduce modulo pi/16."); cos_a = sin_a = dd_real::_nan; return; } dd_real sin_t, cos_t; dd_real s, c; sincos_taylor(t, sin_t, cos_t); if (abs_k == 0) { s = sin_t; c = cos_t; } else { dd_real u = cos_table[abs_k-1]; dd_real v = sin_table[abs_k-1]; if (k > 0) { s = u * sin_t + v * cos_t; c = u * cos_t - v * sin_t; } else { s = u * sin_t - v * cos_t; c = u * cos_t + v * sin_t; } } if (abs_j == 0) { sin_a = s; cos_a = c; } else if (j == 1) { sin_a = c; cos_a = -s; } else if (j == -1) { sin_a = -c; cos_a = s; } else { sin_a = -s; cos_a = -c; } }
dd_real sin(const dd_real &a) { /* Strategy. To compute sin(x), we choose integers a, b so that x = s + a * (pi/2) + b * (pi/16) and |s| <= pi/32. Using the fact that sin(pi/16) = 0.5 * sqrt(2 - sqrt(2 + sqrt(2))) we can compute sin(x) from sin(s), cos(s). This greatly increases the convergence of the sine Taylor series. */ if (a.is_zero()) { return 0.0; } // approximately reduce modulo 2*pi dd_real z = nint(a / dd_real::_2pi); dd_real r = a - dd_real::_2pi * z; // approximately reduce modulo pi/2 and then modulo pi/16. dd_real t; double q = std::floor(r.x[0] / dd_real::_pi2.x[0] + 0.5); t = r - dd_real::_pi2 * q; int j = static_cast<int>(q); q = std::floor(t.x[0] / _pi16.x[0] + 0.5); t -= _pi16 * q; int k = static_cast<int>(q); int abs_k = std::abs(k); if (j < -2 || j > 2) { dd_real::error("(dd_real::sin): Cannot reduce modulo pi/2."); return dd_real::_nan; } if (abs_k > 4) { dd_real::error("(dd_real::sin): Cannot reduce modulo pi/16."); return dd_real::_nan; } if (k == 0) { switch (j) { case 0: return sin_taylor(t); case 1: return cos_taylor(t); case -1: return -cos_taylor(t); default: return -sin_taylor(t); } } dd_real u = cos_table[abs_k-1]; dd_real v = sin_table[abs_k-1]; dd_real sin_t, cos_t; sincos_taylor(t, sin_t, cos_t); if (j == 0) { if (k > 0) { r = u * sin_t + v * cos_t; } else { r = u * sin_t - v * cos_t; } } else if (j == 1) { if (k > 0) { r = u * cos_t - v * sin_t; } else { r = u * cos_t + v * sin_t; } } else if (j == -1) { if (k > 0) { r = v * sin_t - u * cos_t; } else if (k < 0) { r = -u * cos_t - v * sin_t; } } else { if (k > 0) { r = -u * sin_t - v * cos_t; } else { r = v * cos_t - u * sin_t; } } return r; }