Last active
May 22, 2024 19:43
-
-
Save aliakseis/3f92a73da2ea6c85683b58e9dced604c to your computer and use it in GitHub Desktop.
Improved fast atan2 and hypot approximations
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
double fast_atan(double x) { | |
return x / (1 + 0.3211 * x * x); | |
} | |
double do_fast_atan2(double y, double x, bool abs_x_lt_abs_y) { | |
if (abs_x_lt_abs_y) { | |
double z = x / y; | |
double atan_z = fast_atan(z); | |
if (y > 0.0) { | |
return -atan_z + M_PI / 2; | |
} | |
else { | |
return -atan_z - M_PI / 2; | |
} | |
} | |
double z = y / x; | |
double atan_z = fast_atan(z); | |
if (x > 0.0) { | |
return atan_z; | |
} | |
else if (y >= 0.0) { | |
return atan_z + M_PI; | |
} | |
return atan_z - M_PI; | |
} | |
double do_fast_atan2_rotated(double y, double x) { | |
if (fabs(x) < fabs(y)) { | |
double z = x / y; | |
double atan_z = fast_atan(z); | |
if (y > 0.0) { | |
return -atan_z + M_PI / 2 - M_PI / 4; | |
} | |
else { | |
return -atan_z - M_PI / 2 - M_PI / 4; | |
} | |
} | |
double z = y / x; | |
double atan_z = fast_atan(z); | |
if (x > 0.0) { | |
return atan_z - M_PI / 4; | |
} | |
return atan_z + M_PI - M_PI / 4; | |
} | |
// The maximum absolute error across all intervals is less than 0.00012. | |
double fast_atan2(double y, double x) { | |
const double TAN_PI_OVER_8 = 0.41421356237; | |
if (x == 0.0) { | |
return (y > 0.0) ? M_PI / 2 : ((y < 0.0) ? -M_PI / 2 : 0.0); | |
} | |
double abs_x = fabs(x); | |
double abs_y = fabs(y); | |
bool abs_x_lt_abs_y = abs_x < abs_y; | |
if (abs_x_lt_abs_y ? abs_x > TAN_PI_OVER_8 * abs_y : abs_y > TAN_PI_OVER_8 * abs_x) { | |
double x_ = x - y; | |
double y_ = x + y; | |
return do_fast_atan2_rotated(y_, x_); | |
} | |
return do_fast_atan2(y, x, abs_x_lt_abs_y); | |
} | |
double fast_hypot(double x, double y) { | |
x = std::abs(x); | |
y = std::abs(y); | |
auto t = std::min(x, y); | |
x = std::max(x, y); | |
y = t; | |
t = t / x; | |
const double THRESHOLD = M_SQRT2 - 1.0; | |
if (t > THRESHOLD) | |
{ | |
auto x_ = x + y; | |
auto y_ = x - y; | |
t = y_ / x_; | |
return x_ * (1 + t * t / 2) * M_SQRT1_2; | |
} | |
return x * (1 + t * t / 2); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Approximations of the atan2 and hypot functions are commonly found in real-time signal processing applications. Numerous algorithms are available to implement the atan2 and hypot functions. Most solutions are based on the approximation interval [−1, 1]. However, for arguments close to one, this is inefficient. It turns out that the approximation interval can simply be reduced to [-tan(Pi/8), tan(Pi/8)] by rotating the vector formed by X and Y by 45 degrees.