gcem
gcem copied to clipboard
gcem::atan2 deviates from std::atan2
https://en.cppreference.com/w/cpp/numeric/math/atan2
// Define a small epsilon for floating-point comparisons
const double EPSILON = 1e-5;
#if (CONFIG_USE_GCEM)
namespace math
{
float atan2f(float y, float x) { return gcem::atan2(x,y); }
}
#else
namespace math
{
float atan2f(float y, float x) { return std::atan2(x,y); }
}
#endif
/*
* On success, these functions return the principal value of the arc tangent of y/x in radians; the return value is in the range [-pi, pi].
*
* If y is +0 (-0) and x is less than 0, +pi (-pi) is returned.
*
* If y is +0 (-0) and x is greater than 0, +0 (-0) is returned.
*
* If y is less than 0 and x is +0 or -0, -pi/2 is returned.
*
* If y is greater than 0 and x is +0 or -0, pi/2 is returned.
*
* If either x or y is NaN, a NaN is returned.
*
* If y is +0 (-0) and x is -0, +pi (-pi) is returned.
*
* If y is +0 (-0) and x is +0, +0 (-0) is returned.
*
* If y is a finite value greater (less) than 0, and x is negative infinity, +pi (-pi) is returned.
*
* If y is a finite value greater (less) than 0, and x is positive infinity, +0 (-0) is returned.
*
* If y is positive infinity (negative infinity), and x is finite, pi/2 (-pi/2) is returned.
*
* If y is positive infinity (negative infinity) and x is negative infinity, +3*pi/4 (-3*pi/4) is returned.
*
* If y is positive infinity (negative infinity) and x is positive infinity, +pi/4 (-pi/4) is returned.
*
*/
// Test case for general positive values in the first quadrant
TEST(TestMath, PositiveValuesQuadrant1) {
EXPECT_NEAR(math::atan2f(1.0, 1.0), M_PI / 4.0, EPSILON);
EXPECT_NEAR(math::atan2f(0.5, 0.5), M_PI / 4.0, EPSILON);
EXPECT_NEAR(math::atan2f(2.0, 1.0), std::atan(2.0), EPSILON); // y > x
EXPECT_NEAR(math::atan2f(1.0, 2.0), std::atan(0.5), EPSILON); // x > y
}
// Test case for general values in the second quadrant (y > 0, x < 0)
TEST(TestMath, Quadrant2) {
EXPECT_NEAR(math::atan2f(1.0, -1.0), 3.0 * M_PI / 4.0, EPSILON);
EXPECT_NEAR(math::atan2f(0.5, -0.5), 3.0 * M_PI / 4.0, EPSILON);
EXPECT_NEAR(math::atan2f(2.0, -1.0), M_PI - std::atan(2.0), EPSILON);
EXPECT_NEAR(math::atan2f(1.0, -2.0), M_PI - std::atan(0.5), EPSILON);
}
// Test case for general values in the third quadrant (y < 0, x < 0)
TEST(TestMath, Quadrant3) {
EXPECT_NEAR(math::atan2f(-1.0, -1.0), -3.0 * M_PI / 4.0, EPSILON);
EXPECT_NEAR(math::atan2f(-0.5, -0.5), -3.0 * M_PI / 4.0, EPSILON);
EXPECT_NEAR(math::atan2f(-2.0, -1.0), -M_PI + std::atan(2.0), EPSILON);
EXPECT_NEAR(math::atan2f(-1.0, -2.0), -M_PI + std::atan(0.5), EPSILON);
}
// Test case for general values in the fourth quadrant (y < 0, x > 0)
TEST(TestMath, Quadrant4) {
EXPECT_NEAR(math::atan2f(-1.0, 1.0), -M_PI / 4.0, EPSILON);
EXPECT_NEAR(math::atan2f(-0.5, 0.5), -M_PI / 4.0, EPSILON);
EXPECT_NEAR(math::atan2f(-2.0, 1.0), -std::atan(2.0), EPSILON);
EXPECT_NEAR(math::atan2f(-1.0, 2.0), -std::atan(0.5), EPSILON);
}
// Test cases for points on the positive x-axis (y = 0, x > 0)
TEST(TestMath, PositiveXAxis) {
EXPECT_NEAR(math::atan2f(0.0, 1.0), 0.0, EPSILON);
EXPECT_NEAR(math::atan2f(0.0, 100.0), 0.0, EPSILON);
// atan2(+0, x) where x > 0 is +0
EXPECT_NEAR(math::atan2f(0.0, 5.0), 0.0, EPSILON);
}
// Test cases for points on the negative x-axis (y = 0, x < 0)
TEST(TestMath, NegativeXAxis) {
EXPECT_NEAR(math::atan2f(0.0, -1.0), M_PI, EPSILON);
EXPECT_NEAR(math::atan2f(0.0, -100.0), M_PI, EPSILON);
// atan2(+0, x) where x < 0 is +PI
EXPECT_NEAR(math::atan2f(0.0, -5.0), M_PI, EPSILON);
}
// Test cases for points on the positive y-axis (y > 0, x = 0)
TEST(TestMath, PositiveYAxis) {
EXPECT_NEAR(math::atan2f(1.0, 0.0), M_PI / 2.0, EPSILON);
EXPECT_NEAR(math::atan2f(100.0, 0.0), M_PI / 2.0, EPSILON);
// atan2(y, +0) where y > 0 is +PI/2
EXPECT_NEAR(math::atan2f(5.0, 0.0), M_PI / 2.0, EPSILON);
}
// Test cases for points on the negative y-axis (y < 0, x = 0)
TEST(TestMath, NegativeYAxis) {
EXPECT_NEAR(math::atan2f(-1.0, 0.0), -M_PI / 2.0, EPSILON);
EXPECT_NEAR(math::atan2f(-100.0, 0.0), -M_PI / 2.0, EPSILON);
// atan2(y, +0) where y < 0 is -PI/2
EXPECT_NEAR(math::atan2f(-5.0, 0.0), -M_PI / 2.0, EPSILON);
}
// Test cases for origin (0,0) - special case, typically 0 or NaN depending on implementation/standard
TEST(TestMath, Origin) {
// C++ standard specifies atan2(0,0) is 0.
// However, some implementations might return NaN if x and y are both -0.0
// For standard C++, this should be 0.
EXPECT_NEAR(math::atan2f(0.0, 0.0), 0.0, EPSILON);
}
// Test cases involving infinities
TEST(TestMath, Infinities) {
const double infinity = std::numeric_limits<double>::infinity();
const double neg_infinity = -std::numeric_limits<double>::infinity();
// atan2(+-0, -infinity) is +-PI
EXPECT_NEAR(math::atan2f(0.0, neg_infinity), M_PI, EPSILON);
EXPECT_NEAR(math::atan2f(-0.0, neg_infinity), -M_PI, EPSILON);
// atan2(+-0, +infinity) is +-0
EXPECT_NEAR(math::atan2f(0.0, infinity), 0.0, EPSILON);
EXPECT_NEAR(math::atan2f(-0.0, infinity), -0.0, EPSILON); // Note: -0.0 might be treated as 0.0 by EXPECT_NEAR
// atan2(finite, +infinity) is +-0
EXPECT_NEAR(math::atan2f(1.0, infinity), 0.0, EPSILON);
EXPECT_NEAR(math::atan2f(-1.0, infinity), -0.0, EPSILON);
// atan2(finite, -infinity) is +-PI
EXPECT_NEAR(math::atan2f(1.0, neg_infinity), M_PI, EPSILON);
EXPECT_NEAR(math::atan2f(-1.0, neg_infinity), -M_PI, EPSILON);
// atan2(+-infinity, finite) is +-PI/2
EXPECT_NEAR(math::atan2f(infinity, 1.0), M_PI / 2.0, EPSILON);
EXPECT_NEAR(math::atan2f(neg_infinity, 1.0), -M_PI / 2.0, EPSILON);
// atan2(+-infinity, +infinity) is +-PI/4
EXPECT_NEAR(math::atan2f(infinity, infinity), M_PI / 4.0, EPSILON);
EXPECT_NEAR(math::atan2f(neg_infinity, infinity), -M_PI / 4.0, EPSILON);
// atan2(+-infinity, -infinity) is +-3PI/4
EXPECT_NEAR(math::atan2f(infinity, neg_infinity), 3.0 * M_PI / 4.0, EPSILON);
EXPECT_NEAR(math::atan2f(neg_infinity, neg_infinity), -3.0 * M_PI / 4.0, EPSILON);
}
// Test cases involving NaNs (Not-a-Number)
TEST(TestMath, NaNs) {
const double nan = std::numeric_limits<double>::quiet_NaN();
// If y is NaN, result is NaN (unless x is +-Inf)
ASSERT_TRUE(std::isnan(math::atan2f(nan, 1.0)));
ASSERT_TRUE(std::isnan(math::atan2f(nan, -1.0)));
ASSERT_TRUE(std::isnan(math::atan2f(nan, 0.0)));
ASSERT_TRUE(std::isnan(math::atan2f(nan, nan)));
// If x is NaN, result is NaN (unless y is +-Inf)
ASSERT_TRUE(std::isnan(math::atan2f(1.0, nan)));
ASSERT_TRUE(std::isnan(math::atan2f(-1.0, nan)));
ASSERT_TRUE(std::isnan(math::atan2f(0.0, nan)));
}
// Test with subnormal numbers (numbers very close to zero)
TEST(TestMath, SubnormalNumbers) {
double subnormal_val = std::numeric_limits<double>::denorm_min(); // Smallest positive denormalized value
// Test with subnormal y and normal x
EXPECT_NEAR(math::atan2f(subnormal_val, 1.0), 0.0, EPSILON);
EXPECT_NEAR(math::atan2f(-subnormal_val, 1.0), -0.0, EPSILON);
// Test with normal y and subnormal x
EXPECT_NEAR(math::atan2f(1.0, subnormal_val), M_PI / 2.0, EPSILON);
EXPECT_NEAR(math::atan2f(1.0, -subnormal_val), M_PI / 2.0, EPSILON); // Should be PI/2 as -0.0 is treated correctly
}
// Test with large magnitude numbers
TEST(TestMath, LargeNumbers) {
double large_val = std::numeric_limits<double>::max() / 2.0; // A very large number, not infinity
EXPECT_NEAR(math::atan2f(large_val, large_val), M_PI / 4.0, EPSILON);
EXPECT_NEAR(math::atan2f(-large_val, large_val), -M_PI / 4.0, EPSILON);
EXPECT_NEAR(math::atan2f(large_val, -large_val), 3.0 * M_PI / 4.0, EPSILON);
EXPECT_NEAR(math::atan2f(-large_val, -large_val), -3.0 * M_PI / 4.0, EPSILON);
EXPECT_NEAR(math::atan2f(1.0, large_val), 0.0, EPSILON);
EXPECT_NEAR(math::atan2f(large_val, 1.0), M_PI / 2.0, EPSILON);
}
// Test for signed zeros
TEST(TestMath, SignedZeros) {
// atan2(0, +x) for x > 0
EXPECT_NEAR(math::atan2f(0.0, 1.0), 0.0, EPSILON);
EXPECT_NEAR(math::atan2f(-0.0, 1.0), -0.0, EPSILON); // Should return -0.0
// atan2(0, -x) for x > 0
EXPECT_NEAR(math::atan2f(0.0, -1.0), M_PI, EPSILON);
EXPECT_NEAR(math::atan2f(-0.0, -1.0), -M_PI, EPSILON);
// atan2(+y, 0) for y > 0
EXPECT_NEAR(math::atan2f(1.0, 0.0), M_PI / 2.0, EPSILON);
EXPECT_NEAR(math::atan2f(1.0, -0.0), M_PI / 2.0, EPSILON);
// atan2(-y, 0) for y > 0
EXPECT_NEAR(math::atan2f(-1.0, 0.0), -M_PI / 2.0, EPSILON);
EXPECT_NEAR(math::atan2f(-1.0, -0.0), -M_PI / 2.0, EPSILON);
// atan2(+-0, +-0)
EXPECT_NEAR(math::atan2f(0.0, 0.0), 0.0, EPSILON);
EXPECT_NEAR(math::atan2f(-0.0, 0.0), -0.0, EPSILON);
EXPECT_NEAR(math::atan2f(0.0, -0.0), M_PI, EPSILON);
EXPECT_NEAR(math::atan2f(-0.0, -0.0), -M_PI, EPSILON);
}
``