I'm using Catch2 to check the result of a floating point calculation. It fails with the message:
Failure:
REQUIRE_THAT(quat.R_component_1(), Catch::WithinRel(0.0f, ALLOWED_RELATIVE_ERROR))
with expansion:
0.0 and 0 are within 0.01% of each other
It seems that Catch2 is comparing a floating point value with an integer value, but if have no idea where the integer value is coming from. Or maybe the failure message is not accurate and I'm doing somethin else wrong?
This is the unit test which produces the failure:
constexpr double ALLOWED_RELATIVE_ERROR = 0.0001; // 0.01%
TEST_CASE("Euler angles [-135.0, 45.0, -90.0] are converted correctly")
{
constexpr double PHI_DEG = -135.0; // rotation around x-axis
constexpr double THETA_DEG = 45.0; // rotation around y-axis
constexpr double PSI_DEG = -90.0; // rotation around z-axis
auto quat = eulerYZXToQuaternion(PHI_DEG, THETA_DEG, PSI_DEG);
REQUIRE_THAT(quat.R_component_1(), Catch::WithinRel(0.0, ALLOWED_RELATIVE_ERROR)); // This line fails with the message posted above.
REQUIRE_THAT(quat.R_component_2(), Catch::WithinRel(-0.7071068, ALLOWED_RELATIVE_ERROR));
REQUIRE_THAT(quat.R_component_3(), Catch::WithinRel(0.7071068, ALLOWED_RELATIVE_ERROR));
REQUIRE_THAT(quat.R_component_4(), Catch::WithinRel(0.0, ALLOWED_RELATIVE_ERROR));
}
quat
is of type boost::math::quaternion<double>
These are the functions to be tested:
double degToRad(double degrees) { return degrees * M_PI / 180.0; }
boost::math::quaternion<double> eulerYZXToQuaternion(double phi, double theta, double psi)
{
// Convert angles from degrees to radians
phi = degToRad(phi); // rotation around x-axis
theta = degToRad(theta); // rotation around y-axis
psi = degToRad(psi); // rotation around z-axis
double half_phi = 0.5 * phi;
double half_theta = 0.5 * theta;
double half_psi = 0.5 * psi;
double cos_half_phi = cos(half_phi);
double sin_half_phi = sin(half_phi);
double cos_half_theta = cos(half_theta);
double sin_half_theta = sin(half_theta);
double cos_half_psi = cos(half_psi);
double sin_half_psi = sin(half_psi);
boost::math::quaternion<double> q(1, 0, 0, 0);
// Compute the quaternion components. Rotation in sequence Y, Z, X
q *= boost::math::quaternion<double>(cos_half_theta, 0.0, sin_half_theta, 0.0); // y
q *= boost::math::quaternion<double>(cos_half_psi, 0.0, 0.0, sin_half_psi); // z
q *= boost::math::quaternion<double>(cos_half_phi, sin_half_phi, 0.0, 0.0); // x
return q;
}
When I'm doing the check in the following way it works as expected:
REQUIRE(quat.R_component_1() == Approx(0.0).margin(ALLOWED_RELATIVE_ERROR));
Edit:
When I'm using Catch::WithinAbs()
instead of Catch::WithinRel()
the test passes.
But why does the check on quat.R_component_4()
pass with the relative error check?
constexpr double ALLOWED_RELATIVE_ERROR = 0.0001; // 0.01%
constexpr double ALLOWED_ABSOLUT_ERROR = 1e-9;
TEST_CASE("Euler angles [-135.0, 45.0, -90.0] are converted correctly")
{
constexpr double PHI_DEG = -135.0; // rotation around x-axis
constexpr double THETA_DEG = 45.0; // rotation around y-axis
constexpr double PSI_DEG = -90.0; // rotation around z-axis
auto quat = eulerYZXToQuaternion(PHI_DEG, THETA_DEG, PSI_DEG);
REQUIRE_THAT(quat.R_component_1(), Catch::WithinAbs(0.0, ALLOWED_ABSOLUT_ERROR)); // Changed to Catch::WithinAbs()
REQUIRE_THAT(quat.R_component_2(), Catch::WithinRel(-0.7071068, ALLOWED_RELATIVE_ERROR));
REQUIRE_THAT(quat.R_component_3(), Catch::WithinRel(0.7071068, ALLOWED_RELATIVE_ERROR));
REQUIRE_THAT(quat.R_component_4(), Catch::WithinRel(0.0, ALLOWED_RELATIVE_ERROR));
}