[libc][math] Add test and fix atan2f crashing when flush-denorm-to-zero (FTZ) and denorm-as-zero (DAZ) modes are set. (#112828)
This commit is contained in:
@@ -246,12 +246,18 @@ LLVM_LIBC_FUNCTION(float, atan2f, (float y, float x)) {
|
|||||||
uint32_t y_abs = y_bits.uintval();
|
uint32_t y_abs = y_bits.uintval();
|
||||||
uint32_t max_abs = x_abs > y_abs ? x_abs : y_abs;
|
uint32_t max_abs = x_abs > y_abs ? x_abs : y_abs;
|
||||||
uint32_t min_abs = x_abs <= y_abs ? x_abs : y_abs;
|
uint32_t min_abs = x_abs <= y_abs ? x_abs : y_abs;
|
||||||
|
float num_f = FPBits(min_abs).get_val();
|
||||||
|
float den_f = FPBits(max_abs).get_val();
|
||||||
|
double num_d = static_cast<double>(num_f);
|
||||||
|
double den_d = static_cast<double>(den_f);
|
||||||
|
|
||||||
if (LIBC_UNLIKELY(max_abs >= 0x7f80'0000U || min_abs == 0U)) {
|
if (LIBC_UNLIKELY(max_abs >= 0x7f80'0000U || num_d == 0.0)) {
|
||||||
if (x_bits.is_nan() || y_bits.is_nan())
|
if (x_bits.is_nan() || y_bits.is_nan())
|
||||||
return FPBits::quiet_nan().get_val();
|
return FPBits::quiet_nan().get_val();
|
||||||
size_t x_except = x_abs == 0 ? 0 : (x_abs == 0x7f80'0000 ? 2 : 1);
|
double x_d = static_cast<double>(x);
|
||||||
size_t y_except = y_abs == 0 ? 0 : (y_abs == 0x7f80'0000 ? 2 : 1);
|
double y_d = static_cast<double>(y);
|
||||||
|
size_t x_except = (x_d == 0.0) ? 0 : (x_abs == 0x7f80'0000 ? 2 : 1);
|
||||||
|
size_t y_except = (y_d == 0.0) ? 0 : (y_abs == 0x7f80'0000 ? 2 : 1);
|
||||||
|
|
||||||
// Exceptional cases:
|
// Exceptional cases:
|
||||||
// EXCEPT[y_except][x_except][x_is_neg]
|
// EXCEPT[y_except][x_except][x_is_neg]
|
||||||
@@ -275,8 +281,6 @@ LLVM_LIBC_FUNCTION(float, atan2f, (float y, float x)) {
|
|||||||
bool recip = x_abs < y_abs;
|
bool recip = x_abs < y_abs;
|
||||||
double final_sign = IS_NEG[(x_sign != y_sign) != recip];
|
double final_sign = IS_NEG[(x_sign != y_sign) != recip];
|
||||||
fputil::DoubleDouble const_term = CONST_ADJ[x_sign][y_sign][recip];
|
fputil::DoubleDouble const_term = CONST_ADJ[x_sign][y_sign][recip];
|
||||||
double num_d = static_cast<double>(FPBits(min_abs).get_val());
|
|
||||||
double den_d = static_cast<double>(FPBits(max_abs).get_val());
|
|
||||||
double q_d = num_d / den_d;
|
double q_d = num_d / den_d;
|
||||||
|
|
||||||
double k_d = fputil::nearest_integer(q_d * 0x1.0p4f);
|
double k_d = fputil::nearest_integer(q_d * 0x1.0p4f);
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
#include "src/__support/FPUtil/FPBits.h"
|
#include "src/__support/FPUtil/FPBits.h"
|
||||||
#include "src/__support/FPUtil/fpbits_str.h"
|
#include "src/__support/FPUtil/fpbits_str.h"
|
||||||
#include "src/__support/macros/config.h"
|
#include "src/__support/macros/config.h"
|
||||||
|
#include "src/__support/macros/properties/architectures.h"
|
||||||
#include "test/UnitTest/RoundingModeUtils.h"
|
#include "test/UnitTest/RoundingModeUtils.h"
|
||||||
#include "test/UnitTest/StringUtils.h"
|
#include "test/UnitTest/StringUtils.h"
|
||||||
#include "test/UnitTest/Test.h"
|
#include "test/UnitTest/Test.h"
|
||||||
@@ -192,6 +193,31 @@ template <typename T> struct FPTest : public Test {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Add facility to test Flush-Denormal-To-Zero (FTZ) and Denormal-As-Zero (DAZ)
|
||||||
|
// modes.
|
||||||
|
// These tests to ensure that our implementations will not crash under these
|
||||||
|
// modes.
|
||||||
|
#if defined(LIBC_TARGET_ARCH_IS_X86_64) && __has_builtin(__builtin_ia32_stmxcsr)
|
||||||
|
|
||||||
|
#define LIBC_TEST_FTZ_DAZ
|
||||||
|
|
||||||
|
static constexpr unsigned FTZ = 0x8000; // Flush denormal to zero
|
||||||
|
static constexpr unsigned DAZ = 0x0040; // Denormal as zero
|
||||||
|
|
||||||
|
struct ModifyMXCSR {
|
||||||
|
ModifyMXCSR(unsigned flags) {
|
||||||
|
old_mxcsr = __builtin_ia32_stmxcsr();
|
||||||
|
__builtin_ia32_ldmxcsr(old_mxcsr | flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
~ModifyMXCSR() { __builtin_ia32_ldmxcsr(old_mxcsr); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
unsigned old_mxcsr;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace testing
|
} // namespace testing
|
||||||
} // namespace LIBC_NAMESPACE_DECL
|
} // namespace LIBC_NAMESPACE_DECL
|
||||||
|
|
||||||
|
|||||||
@@ -58,3 +58,40 @@ TEST_F(LlvmLibcAtan2fTest, SpecialNumbers) {
|
|||||||
// EXPECT_FP_EXCEPTION(0);
|
// EXPECT_FP_EXCEPTION(0);
|
||||||
EXPECT_MATH_ERRNO(0);
|
EXPECT_MATH_ERRNO(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef LIBC_TEST_FTZ_DAZ
|
||||||
|
|
||||||
|
using namespace LIBC_NAMESPACE::testing;
|
||||||
|
|
||||||
|
TEST_F(LlvmLibcAtan2fTest, FTZMode) {
|
||||||
|
ModifyMXCSR mxcsr(FTZ);
|
||||||
|
|
||||||
|
EXPECT_FP_EQ(0x1.921fb6p-1f,
|
||||||
|
LIBC_NAMESPACE::atan2f(min_denormal, min_denormal));
|
||||||
|
EXPECT_FP_EQ(0x1.000002p-23f,
|
||||||
|
LIBC_NAMESPACE::atan2f(min_denormal, max_denormal));
|
||||||
|
EXPECT_FP_EQ(0x1.921fb4p0f,
|
||||||
|
LIBC_NAMESPACE::atan2f(max_denormal, min_denormal));
|
||||||
|
EXPECT_FP_EQ(0x1.921fb6p-1f,
|
||||||
|
LIBC_NAMESPACE::atan2f(max_denormal, max_denormal));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(LlvmLibcAtan2fTest, DAZMode) {
|
||||||
|
ModifyMXCSR mxcsr(DAZ);
|
||||||
|
|
||||||
|
EXPECT_FP_EQ(0.0f, LIBC_NAMESPACE::atan2f(min_denormal, min_denormal));
|
||||||
|
EXPECT_FP_EQ(0.0f, LIBC_NAMESPACE::atan2f(min_denormal, max_denormal));
|
||||||
|
EXPECT_FP_EQ(0.0f, LIBC_NAMESPACE::atan2f(max_denormal, min_denormal));
|
||||||
|
EXPECT_FP_EQ(0.0f, LIBC_NAMESPACE::atan2f(max_denormal, max_denormal));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(LlvmLibcAtan2fTest, FTZDAZMode) {
|
||||||
|
ModifyMXCSR mxcsr(FTZ | DAZ);
|
||||||
|
|
||||||
|
EXPECT_FP_EQ(0.0f, LIBC_NAMESPACE::atan2f(min_denormal, min_denormal));
|
||||||
|
EXPECT_FP_EQ(0.0f, LIBC_NAMESPACE::atan2f(min_denormal, max_denormal));
|
||||||
|
EXPECT_FP_EQ(0.0f, LIBC_NAMESPACE::atan2f(max_denormal, min_denormal));
|
||||||
|
EXPECT_FP_EQ(0.0f, LIBC_NAMESPACE::atan2f(max_denormal, max_denormal));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user