[libc][math] Adding LIBC_MATH_ASSUME_ROUND_NEAREST_ONLY option (#201154)

This PR adds a new option ~~`LIBC_MATH_ALWAYS_ROUND_NEAREST`~~
`LIBC_MATH_ASSUME_ROUND_NEAREST_ONLY` to LLVM libm.

There are some UBs that I kept as-is from the original code to keep the
changes non-disruptive (which I've marked with TODO comments)

Benchmarks (from `files.zip` in the comment
https://github.com/llvm/llvm-project/issues/198276#issue-4468816457):
- System libm:
```
overflow (>710)                1.55 ns/call  (644M ops/sec)
underflow to 0 (<-746)         1.34 ns/call  (747M ops/sec)
normal [-10,10]                4.41 ns/call  (227M ops/sec)
denormals [-740,-735]          2.25 ns/call  (444M ops/sec)
near-uflow [-700,-690]         2.25 ns/call  (444M ops/sec)
```
- LLVM libm (without the option being set):
```
overflow (>710)                5.21 ns/call  (192M ops/sec)
underflow to 0 (<-746)         7.20 ns/call  (139M ops/sec)
normal [-10,10]                4.06 ns/call  (246M ops/sec)
denormals [-740,-735]          5.85 ns/call  (171M ops/sec)
near-uflow [-700,-690]         4.06 ns/call  (246M ops/sec)
```
- LLVM libm (with the option being **set**, `bench-exp-paths-inc.cc`)
```
overflow (>710)                1.95 ns/call  (514M ops/sec)
underflow to 0 (<-746)         2.23 ns/call  (449M ops/sec)
normal [-10,10]                3.43 ns/call  (292M ops/sec)
denormals [-740,-735]          5.19 ns/call  (193M ops/sec)
near-uflow [-700,-690]         3.43 ns/call  (292M ops/sec)
```

Notice that in the `bench-exp-paths-inc.cc` file, I've changed the
option name from `LIBC_MATH_ROUND_NEAREST` to
`LIBC_MATH_ASSUME_ROUND_NEAREST_ONLY`

Benchmark system: macOS 26.5, Apple M3.
This commit is contained in:
Hoàng Minh Thiên 2026-06-12 00:42:28 +07:00 committed by GitHub
parent 3b67307500
commit 582643f1ec
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
55 changed files with 885 additions and 277 deletions

View file

@ -144,7 +144,7 @@
"math": {
"LIBC_CONF_MATH_OPTIMIZATIONS": {
"value": 0,
"doc": "Configure optimizations for math functions. Values accepted are LIBC_MATH_SKIP_ACCURATE_PASS, LIBC_MATH_SMALL_TABLES, LIBC_MATH_NO_ERRNO, LIBC_MATH_NO_EXCEPT, LIBC_MATH_FAST, and LIBC_MATH_INTERMEDIATE_COMP_IN_FLOAT."
"doc": "Configure optimizations for math functions. Values accepted are LIBC_MATH_SKIP_ACCURATE_PASS, LIBC_MATH_SMALL_TABLES, LIBC_MATH_NO_ERRNO, LIBC_MATH_NO_EXCEPT, LIBC_MATH_FAST, LIBC_MATH_INTERMEDIATE_COMP_IN_FLOAT, and LIBC_MATH_ASSUME_ROUND_NEAREST_ONLY."
},
"LIBC_CONF_FREXP_INF_NAN_EXPONENT": {
"value": "",

View file

@ -199,10 +199,14 @@ LIBC_INLINE T hypot(T x, T y) {
sum >>= 2;
++out_exp;
if (out_exp >= FPBits_t::MAX_BIASED_EXPONENT) {
#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
if (int round_mode = quick_get_round();
round_mode == FE_TONEAREST || round_mode == FE_UPWARD)
return FPBits_t::inf().get_val();
return FPBits_t::max_normal().get_val();
#else
return FPBits_t::inf().get_val();
#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
}
} else {
// For denormal result, we simply move the leading bit of the result to
@ -241,6 +245,17 @@ LIBC_INLINE T hypot(T x, T y) {
y_new >>= 1;
#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
// Round to the nearest, tie to even.
if (round_bit && (lsb || sticky_bits || (r != 0)))
++y_new;
if (y_new >= (ONE >> 1)) {
y_new -= ONE >> 1;
++out_exp;
if (out_exp >= FPBits_t::MAX_BIASED_EXPONENT)
return FPBits_t::inf().get_val();
}
#else
// Round to the nearest, tie to even.
int round_mode = quick_get_round();
switch (round_mode) {
@ -264,6 +279,7 @@ LIBC_INLINE T hypot(T x, T y) {
return FPBits_t::max_normal().get_val();
}
}
#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
y_new |= static_cast<StorageType>(out_exp) << FPBits_t::FRACTION_LEN;

View file

@ -170,14 +170,17 @@ ldexp(T x, U exp) {
FPBits<T>::MAX_BIASED_EXPONENT + FPBits<T>::FRACTION_LEN + 1;
// Make sure that we can safely cast exp to int when not returning early.
static_assert(EXP_LIMIT <= INT_MAX && -EXP_LIMIT >= INT_MIN);
if (LIBC_UNLIKELY(exp > EXP_LIMIT)) {
int rounding_mode = quick_get_round();
Sign sign = bits.sign();
#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
int rounding_mode = quick_get_round();
if ((sign == Sign::POS && rounding_mode == FE_DOWNWARD) ||
(sign == Sign::NEG && rounding_mode == FE_UPWARD) ||
(rounding_mode == FE_TOWARDZERO))
return FPBits<T>::max_normal(sign).get_val();
#endif
set_errno_if_required(ERANGE);
raise_except_if_required(FE_OVERFLOW);
@ -186,12 +189,13 @@ ldexp(T x, U exp) {
// Similarly on the negative side we return zero early if |exp| is too small.
if (LIBC_UNLIKELY(exp < -EXP_LIMIT)) {
int rounding_mode = quick_get_round();
Sign sign = bits.sign();
#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
int rounding_mode = quick_get_round();
if ((sign == Sign::POS && rounding_mode == FE_UPWARD) ||
(sign == Sign::NEG && rounding_mode == FE_DOWNWARD))
return FPBits<T>::min_subnormal(sign).get_val();
#endif
set_errno_if_required(ERANGE);
raise_except_if_required(FE_UNDERFLOW);

View file

@ -246,6 +246,9 @@ round_using_specific_rounding_mode(T x, int rnd) {
template <typename T>
LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_floating_point_v<T>, T>
round_using_current_rounding_mode(T x) {
#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
return round_using_specific_rounding_mode(x, FP_INT_TONEAREST);
#else // !LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
int rounding_mode = quick_get_round();
switch (rounding_mode) {
@ -260,6 +263,7 @@ round_using_current_rounding_mode(T x) {
default:
__builtin_unreachable();
}
#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
}
template <bool IsSigned, typename T>

View file

@ -17,6 +17,7 @@
#include "rounding_mode.h"
#include "src/__support/CPP/type_traits.h"
#include "src/__support/big_int.h"
#include "src/__support/macros/attributes.h"
#include "src/__support/macros/config.h"
#include "src/__support/macros/optimization.h" // LIBC_UNLIKELY
#include "src/__support/macros/properties/types.h"
@ -41,11 +42,26 @@ namespace fputil {
template <size_t Bits>
LIBC_INLINE LIBC_CONSTEXPR_DEFAULT int
rounding_direction(const LIBC_NAMESPACE::UInt<Bits> &value, size_t rshift,
Sign logical_sign) {
[[maybe_unused]] Sign logical_sign) {
// logical_sign only affects FE_DOWNWARD and FE_UPWARD rounding modes. In the
// case of LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY being enabled, this option
// is no-op
if (rshift == 0 || (rshift < Bits && (value << (Bits - rshift)) == 0) ||
(rshift >= Bits && value == 0))
return 0; // exact
#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
if (rshift > 0 && rshift <= Bits && value.get_bit(rshift - 1)) {
// We round up, unless the value is an exact halfway case and
// the bit that will end up in the units place is 0, in which
// case tie-break-to-even says round down.
bool round_bit = rshift < Bits ? value.get_bit(rshift) : 0;
return round_bit != 0 || (value << (Bits - rshift + 1)) != 0 ? +1 : -1;
} else {
return -1;
}
#else // !LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
switch (quick_get_round()) {
case FE_TONEAREST:
if (rshift > 0 && rshift <= Bits && value.get_bit(rshift - 1)) {
@ -72,6 +88,7 @@ rounding_direction(const LIBC_NAMESPACE::UInt<Bits> &value, size_t rshift,
default:
__builtin_unreachable();
}
#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
}
// A generic class to perform computations of high precision floating points.
@ -190,6 +207,9 @@ template <size_t Bits> struct DyadicFloat {
raise_except_if_required(FE_OVERFLOW | FE_INEXACT);
}
#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
return FPBits::inf(sign).get_val();
#else // !LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
switch (quick_get_round()) {
case FE_TONEAREST:
return FPBits::inf(sign).get_val();
@ -206,6 +226,7 @@ template <size_t Bits> struct DyadicFloat {
default:
__builtin_unreachable();
}
#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
}
StorageType out_biased_exp = 0;
@ -248,6 +269,10 @@ template <size_t Bits> struct DyadicFloat {
StorageType result =
FPBits::create_value(sign, out_biased_exp, out_mantissa).uintval();
#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
if (round && (lsb || sticky))
++result;
#else
switch (quick_get_round()) {
case FE_TONEAREST:
if (round && (lsb || sticky))
@ -264,6 +289,7 @@ template <size_t Bits> struct DyadicFloat {
default:
break;
}
#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
if (ShouldSignalExceptions && (round || sticky)) {
int excepts = FE_INEXACT;
@ -385,14 +411,14 @@ template <size_t Bits> struct DyadicFloat {
output_bits_t r_bits = FPBits<T>(r).uintval() - clear_exp;
if (!(r_bits & FPBits<T>::EXP_MASK)) {
// Output is denormal after rounding, clear the implicit bit for 80-bit
// long double.
// Output is denormal after rounding, clear the implicit bit for
// 80-bit long double.
r_bits -= IMPLICIT_MASK;
// TODO: IEEE Std 754-2019 lets implementers choose whether to check for
// "tininess" before or after rounding for base-2 formats, as long as
// the same choice is made for all operations. Our choice to check after
// rounding might not be the same as the hardware's.
// TODO: IEEE Std 754-2019 lets implementers choose whether to check
// for "tininess" before or after rounding for base-2 formats, as long
// as the same choice is made for all operations. Our choice to check
// after rounding might not be the same as the hardware's.
if (ShouldSignalExceptions && round_and_sticky) {
set_errno_if_required(ERANGE);
raise_except_if_required(FE_UNDERFLOW);
@ -465,9 +491,9 @@ template <size_t Bits> struct DyadicFloat {
new_mant <<= exponent;
} else if (exponent < 0) {
// Cast the exponent to size_t before negating it, rather than after,
// to avoid undefined behavior negating INT_MIN as an integer (although
// exponents coming in to this function _shouldn't_ be that large). The
// result should always end up as a positive size_t.
// to avoid undefined behavior negating INT_MIN as an integer
// (although exponents coming in to this function _shouldn't_ be that
// large). The result should always end up as a positive size_t.
size_t shift = -static_cast<size_t>(exponent);
if (shift >= Bits)
new_mant = 0;
@ -494,8 +520,8 @@ template <size_t Bits> struct DyadicFloat {
}
};
// Quick add - Add 2 dyadic floats with rounding toward 0 and then normalize the
// output:
// Quick add - Add 2 dyadic floats with rounding toward 0 and then normalize
// the output:
// - Align the exponents so that:
// new a.exponent = new b.exponent = max(a.exponent, b.exponent)
// - Add or subtract the mantissas depending on the signs.
@ -503,9 +529,9 @@ template <size_t Bits> struct DyadicFloat {
// The absolute errors compared to the mathematical sum is bounded by:
// | quick_add(a, b) - (a + b) | < MSB(a + b) * 2^(-Bits + 2),
// i.e., errors are up to 2 ULPs.
// Assume inputs are normalized (by constructors or other functions) so that we
// don't need to normalize the inputs again in this function. If the inputs are
// not normalized, the results might lose precision significantly.
// Assume inputs are normalized (by constructors or other functions) so that
// we don't need to normalize the inputs again in this function. If the
// inputs are not normalized, the results might lose precision significantly.
template <size_t Bits>
LIBC_INLINE constexpr DyadicFloat<Bits> quick_add(DyadicFloat<Bits> a,
DyadicFloat<Bits> b) {
@ -564,9 +590,9 @@ LIBC_INLINE constexpr DyadicFloat<Bits> quick_sub(DyadicFloat<Bits> a,
// ~ (full product a.mantissa * b.mantissa) >> Bits.
// The errors compared to the mathematical product is bounded by:
// 2 * errors of quick_mul_hi = 2 * (UInt<Bits>::WORD_COUNT - 1) in ULPs.
// Assume inputs are normalized (by constructors or other functions) so that we
// don't need to normalize the inputs again in this function. If the inputs are
// not normalized, the results might lose precision significantly.
// Assume inputs are normalized (by constructors or other functions) so that
// we don't need to normalize the inputs again in this function. If the
// inputs are not normalized, the results might lose precision significantly.
template <size_t Bits>
LIBC_INLINE constexpr DyadicFloat<Bits> quick_mul(const DyadicFloat<Bits> &a,
const DyadicFloat<Bits> &b) {
@ -606,8 +632,8 @@ rounded_mul(const DyadicFloat<Bits> &a, const DyadicFloat<Bits> &b) {
return DyadicFloat<Bits>::round(result_sign, result_exponent, product, Bits);
}
// Approximate reciprocal - given a nonzero a, make a good approximation to 1/a.
// The method is Newton-Raphson iteration, based on quick_mul.
// Approximate reciprocal - given a nonzero a, make a good approximation to
// 1/a. The method is Newton-Raphson iteration, based on quick_mul.
template <size_t Bits, typename = cpp::enable_if_t<(Bits >= 32)>>
LIBC_INLINE constexpr DyadicFloat<Bits>
approx_reciprocal(const DyadicFloat<Bits> &a) {
@ -616,8 +642,8 @@ approx_reciprocal(const DyadicFloat<Bits> &a) {
// You can derive this by using the Newton-Raphson formula with the function
// f(x) = 1/x - a. But another way to see that it works is to say: suppose
// that ax = 1-e for some small error e. Then ax' = ax(2-ax) = (1-e)(1+e) =
// 1-e^2. So the error in x' is the square of the error in x, i.e. the number
// of correct bits in x' is double the number in x.
// 1-e^2. So the error in x' is the square of the error in x, i.e. the
// number of correct bits in x' is double the number in x.
// An initial approximation to the reciprocal
DyadicFloat<Bits> x(Sign::POS, -32 - a.exponent - int(Bits),

View file

@ -58,6 +58,9 @@ template <typename T, size_t N> struct ExceptValues {
for (size_t i = 0; i < N; ++i) {
if (LIBC_UNLIKELY(x_bits == values[i].input)) {
StorageType out_bits = values[i].rnd_towardzero_result;
#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
out_bits += values[i].rnd_tonearest_offset;
#else
switch (fputil::quick_get_round()) {
case FE_UPWARD:
out_bits += values[i].rnd_upward_offset;
@ -69,6 +72,7 @@ template <typename T, size_t N> struct ExceptValues {
out_bits += values[i].rnd_tonearest_offset;
break;
}
#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
return FPBits<T>(out_bits).get_val();
}
}
@ -80,6 +84,9 @@ template <typename T, size_t N> struct ExceptValues {
for (size_t i = 0; i < N; ++i) {
if (LIBC_UNLIKELY(x_abs == values[i].input)) {
StorageType out_bits = values[i].rnd_towardzero_result;
#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
out_bits += values[i].rnd_tonearest_offset;
#else
switch (fputil::quick_get_round()) {
case FE_UPWARD:
if (sign)
@ -100,6 +107,7 @@ template <typename T, size_t N> struct ExceptValues {
out_bits += values[i].rnd_tonearest_offset;
break;
}
#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
T result = FPBits<T>(out_bits).get_val();
if (sign)
result = -result;
@ -114,24 +122,38 @@ template <typename T, size_t N> struct ExceptValues {
// Helper functions to set results for exceptional cases.
template <typename T>
LIBC_INLINE LIBC_CONSTEXPR_DEFAULT T round_result_slightly_down(T value_rn) {
#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
fputil::raise_except_if_required(FE_INEXACT);
return value_rn;
#else
if (cpp::is_constant_evaluated()) {
fputil::raise_except_if_required(FE_INEXACT);
return value_rn;
} else {
volatile T tmp = value_rn;
tmp -= FPBits<T>::min_normal().get_val();
fputil::raise_except_if_required(FE_INEXACT);
return tmp;
}
#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
}
template <typename T>
LIBC_INLINE LIBC_CONSTEXPR_DEFAULT T round_result_slightly_up(T value_rn) {
#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
fputil::raise_except_if_required(FE_INEXACT);
return value_rn;
#else
if (cpp::is_constant_evaluated()) {
fputil::raise_except_if_required(FE_INEXACT);
return value_rn;
} else {
volatile T tmp = value_rn;
tmp += FPBits<T>::min_normal().get_val();
fputil::raise_except_if_required(FE_INEXACT);
return tmp;
}
#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
}
#if defined(LIBC_TYPES_HAS_FLOAT16) && \
@ -139,25 +161,37 @@ LIBC_INLINE LIBC_CONSTEXPR_DEFAULT T round_result_slightly_up(T value_rn) {
template <>
LIBC_INLINE LIBC_CONSTEXPR_DEFAULT float16
round_result_slightly_down(float16 value_rn) {
#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
fputil::raise_except_if_required(FE_INEXACT);
return value_rn;
#else
if (cpp::is_constant_evaluated()) {
return value_rn;
} else {
volatile float tmp = value_rn;
tmp -= FPBits<float16>::min_normal().get_val();
fputil::raise_except_if_required(FE_INEXACT);
return cast<float16>(tmp);
}
#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
}
template <>
LIBC_INLINE LIBC_CONSTEXPR_DEFAULT float16
round_result_slightly_up(float16 value_rn) {
#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
fputil::raise_except_if_required(FE_INEXACT);
return value_rn;
#else
if (cpp::is_constant_evaluated()) {
return value_rn;
} else {
volatile float tmp = value_rn;
tmp += FPBits<float16>::min_normal().get_val();
fputil::raise_except_if_required(FE_INEXACT);
return cast<float16>(tmp);
}
#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
}
#endif

View file

@ -275,10 +275,14 @@ fma(InType x, InType y, InType z) {
if (prod_mant == 0) {
// When there is exact cancellation, i.e., x*y == -z exactly, return -0.0 if
// rounding downward and +0.0 for other rounding modes.
#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
prod_sign = Sign::POS;
#else
if (fputil::quick_get_round() == FE_DOWNWARD)
prod_sign = Sign::NEG;
else
prod_sign = Sign::POS;
#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
}
DyadicFloat result(prod_sign, prod_lsb_exp - InFPBits::EXP_BIAS, prod_mant);

View file

@ -98,12 +98,16 @@ add_or_sub(InType x, InType y) {
if (y_bits.is_zero()) {
if (is_effectively_add)
return OutFPBits::zero(x_bits.sign()).get_val();
#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
return OutFPBits::zero(Sign::POS).get_val();
#else
switch (fputil::quick_get_round()) {
case FE_DOWNWARD:
return OutFPBits::zero(Sign::NEG).get_val();
default:
return OutFPBits::zero(Sign::POS).get_val();
}
#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
}
if constexpr (cpp::is_same_v<InType, bfloat16> &&
@ -136,12 +140,16 @@ add_or_sub(InType x, InType y) {
InType y_abs = y_bits.abs().get_val();
if (x_abs == y_abs && !is_effectively_add) {
#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
return OutFPBits::zero(Sign::POS).get_val();
#else
switch (fputil::quick_get_round()) {
case FE_DOWNWARD:
return OutFPBits::zero(Sign::NEG).get_val();
default:
return OutFPBits::zero(Sign::POS).get_val();
}
#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
}
Sign result_sign = Sign::POS;

View file

@ -110,7 +110,11 @@ LIBC_INLINE LIBC_CONSTEXPR_DEFAULT long double sqrt(long double x) {
// Append the exponent field.
x_exp = ((x_exp >> 1) + LDBits::EXP_BIAS);
y |= (static_cast<StorageType>(x_exp) << (LDBits::FRACTION_LEN + 1));
#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
// Round to nearest, ties to even
if (rb && (lsb || (r != 0)))
++y;
#else
switch (quick_get_round()) {
case FE_TONEAREST:
// Round to nearest, ties to even
@ -122,6 +126,7 @@ LIBC_INLINE LIBC_CONSTEXPR_DEFAULT long double sqrt(long double x) {
++y;
break;
}
#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
// Extract output
FPBits<long double> out(0.0L);

View file

@ -24,8 +24,12 @@ namespace generic {
// 1.0f + 2^-25 = 1.0f for FE_TONEAREST, FE_DOWNWARD, FE_TOWARDZERO
// = 0x1.000002f for FE_UPWARD.
LIBC_INLINE bool fenv_is_round_up() {
#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
return false;
#else
static volatile float x = 0x1.0p-25f;
return (1.0f + x != 1.0f);
#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
}
// Quick free-standing test whether fegetround() == FE_DOWNWARD.
@ -33,8 +37,12 @@ LIBC_INLINE bool fenv_is_round_up() {
// -1.0f - 2^-25 = -1.0f for FE_TONEAREST, FE_UPWARD, FE_TOWARDZERO
// = -0x1.000002f for FE_DOWNWARD.
LIBC_INLINE bool fenv_is_round_down() {
#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
return false;
#else
static volatile float x = 0x1.0p-25f;
return (-1.0f - x != -1.0f);
#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
}
// Quick free-standing test whether fegetround() == FE_TONEAREST.
@ -44,9 +52,13 @@ LIBC_INLINE bool fenv_is_round_down() {
// 1.5f - 2^-24 = 1.5f for FE_TONEAREST, FE_UPWARD
// = 0x1.0ffffep-1f for FE_DOWNWARD, FE_TOWARDZERO
LIBC_INLINE bool fenv_is_round_to_nearest() {
#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
return true;
#else
static volatile float x = 0x1.0p-24f;
float y = 1.5f + x;
return (y == 1.5f - x);
#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
}
// Quick free-standing test whether fegetround() == FE_TOWARDZERO.
@ -60,13 +72,20 @@ LIBC_INLINE bool fenv_is_round_to_nearest() {
// = 2^-22 for FE_TONEAREST, FE_UPWARD
// = 0 for FE_DOWNWARD
LIBC_INLINE bool fenv_is_round_to_zero() {
#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
return false;
#else
static volatile float x = 0x1.0p-24f;
float y = x;
return ((0x1.000002p0f + y) + (-1.0f - y) == 0x1.0p-23f);
#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
}
// Quick free standing get rounding mode based on the above observations.
LIBC_INLINE int quick_get_round() {
#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
return FE_TONEAREST;
#else
static volatile float x = 0x1.0p-24f;
float y = x;
float z = (0x1.000002p0f + y) + (-1.0f - y);
@ -76,6 +95,7 @@ LIBC_INLINE int quick_get_round() {
if (z == 0x1.0p-23f)
return FE_TOWARDZERO;
return (2.0f + y == 2.0f) ? FE_TONEAREST : FE_UPWARD;
#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
}
} // namespace generic

View file

@ -52,10 +52,12 @@ LIBC_INLINE constexpr bool expects_bool_condition(T value, T expected) {
#define LIBC_MATH_SMALL_TABLES 0x02
#define LIBC_MATH_NO_ERRNO 0x04
#define LIBC_MATH_NO_EXCEPT 0x08
#define LIBC_MATH_INTERMEDIATE_COMP_IN_FLOAT 0x10
#define LIBC_MATH_ASSUME_ROUND_NEAREST_ONLY 0x20
#define LIBC_MATH_FAST \
(LIBC_MATH_SKIP_ACCURATE_PASS | LIBC_MATH_SMALL_TABLES | \
LIBC_MATH_NO_ERRNO | LIBC_MATH_NO_EXCEPT)
#define LIBC_MATH_INTERMEDIATE_COMP_IN_FLOAT 0x10
LIBC_MATH_NO_ERRNO | LIBC_MATH_NO_EXCEPT | \
LIBC_MATH_ASSUME_ROUND_NEAREST_ONLY)
#ifndef LIBC_MATH
#define LIBC_MATH 0
@ -81,4 +83,8 @@ LIBC_INLINE constexpr bool expects_bool_condition(T value, T expected) {
#define LIBC_MATH_HAS_NO_EXCEPT
#endif
#if ((LIBC_MATH) & LIBC_MATH_ASSUME_ROUND_NEAREST_ONLY)
#define LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
#endif
#endif // LLVM_LIBC_SRC___SUPPORT_MACROS_OPTIMIZATION_H

View file

@ -45,11 +45,13 @@ LIBC_INLINE LIBC_CONSTEXPR bfloat16 asinbf16(bfloat16 x) {
return x; // with sign
if (LIBC_UNLIKELY(x_abs <= 0x3D00)) {
#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
int rounding = fputil::quick_get_round();
if ((xbits.is_pos() && rounding == FE_UPWARD) ||
(xbits.is_neg() && rounding == FE_DOWNWARD)) {
return fputil::cast<bfloat16>(fputil::multiply_add(xf, 0x1.0p-9f, xf));
}
#endif
return x;
}

View file

@ -73,11 +73,13 @@ LIBC_INLINE constexpr float16 asinf16(float16 x) {
// else, in other rounding modes,
// asin(x) = x
if (LIBC_UNLIKELY(x_abs <= 0x1a1e)) {
#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
int rounding = fputil::quick_get_round();
if ((xbits.is_pos() && rounding == FE_UPWARD) ||
(xbits.is_neg() && rounding == FE_DOWNWARD))
return fputil::cast<float16>(fputil::multiply_add(xf, 0x1.0p-11f, xf));
#endif
return x;
}

View file

@ -87,11 +87,13 @@ LIBC_INLINE constexpr float16 asinhf16(float16 x) {
// when |x| < 0x1.718p-5, asinhf16(x) = x. Adjust by 1 ULP for certain
// rounding types.
if (LIBC_UNLIKELY(x_abs < 0x29c6)) {
#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
int rounding = fputil::quick_get_round();
if ((rounding == FE_UPWARD || rounding == FE_TOWARDZERO) && xf < 0)
return fputil::cast<float16>(xf + 0x1p-24f);
if ((rounding == FE_DOWNWARD || rounding == FE_TOWARDZERO) && xf > 0)
return fputil::cast<float16>(xf - 0x1p-24f);
#endif
return fputil::cast<float16>(xf);
}

View file

@ -84,6 +84,10 @@ LIBC_INLINE double asinpi(double x) {
MantT sticky_mask = (MantT(1) << (SHIFT_53 - 1)) - 1;
bool sticky = (r.mantissa & sticky_mask) != 0;
bool lsb = static_cast<bool>(m53 & 1);
#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
// Carry if round_bit && (lsb || sticky) (round half to even).
raise_underflow = !(round_bit && (lsb || sticky));
#else
switch (fputil::quick_get_round()) {
case FE_TONEAREST:
// Carry if round_bit && (lsb || sticky) (round half to even).
@ -100,6 +104,7 @@ LIBC_INLINE double asinpi(double x) {
raise_underflow = true; // truncation never carries
break;
}
#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
}
}
if (raise_underflow)

View file

@ -40,9 +40,11 @@ LIBC_INLINE float coshf(float x) {
if (xbits.is_inf_or_nan())
return x + FPBits::inf().get_val();
#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
int rounding = fputil::quick_get_round();
if (LIBC_UNLIKELY(rounding == FE_DOWNWARD || rounding == FE_TOWARDZERO))
return FPBits::max_normal().get_val();
#endif
fputil::set_errno_if_required(ERANGE);
fputil::raise_except_if_required(FE_OVERFLOW);

View file

@ -90,6 +90,11 @@ LIBC_INLINE constexpr float16 coshf16(float16 x) {
if (x_bits.is_inf())
return FPBits::inf().get_val();
#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
fputil::set_errno_if_required(ERANGE);
fputil::raise_except_if_required(FE_OVERFLOW | FE_INEXACT);
return FPBits::inf().get_val();
#else
switch (fputil::quick_get_round()) {
case FE_TONEAREST:
case FE_UPWARD:
@ -99,6 +104,7 @@ LIBC_INLINE constexpr float16 coshf16(float16 x) {
default:
return FPBits::max_normal().get_val();
}
#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
}
}

View file

@ -105,8 +105,10 @@ LIBC_INLINE float16 erfcf16(float16 x) {
}
fputil::set_errno_if_required(ERANGE);
fputil::raise_except_if_required(FE_UNDERFLOW | FE_INEXACT);
#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
if (fputil::fenv_is_round_up())
return FPBits::min_subnormal().get_val();
#endif
return 0.0f16;
}

View file

@ -211,8 +211,10 @@ LIBC_INLINE double set_exceptional(double x) {
if (xbits.is_nan())
return x;
#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
if (fputil::quick_get_round() == FE_UPWARD)
return FPBits::min_subnormal().get_val();
#endif
fputil::set_errno_if_required(ERANGE);
fputil::raise_except_if_required(FE_UNDERFLOW);
return 0.0;
@ -221,9 +223,11 @@ LIBC_INLINE double set_exceptional(double x) {
// x >= round(log(MAX_NORMAL), D, RU) = 0x1.62e42fefa39fp+9 or +inf/nan
// x is finite
if (x_u < 0x7ff0'0000'0000'0000ULL) {
#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
int rounding = fputil::quick_get_round();
if (rounding == FE_DOWNWARD || rounding == FE_TOWARDZERO)
return FPBits::max_normal().get_val();
#endif
fputil::set_errno_if_required(ERANGE);
fputil::raise_except_if_required(FE_OVERFLOW);

View file

@ -259,8 +259,10 @@ LIBC_INLINE double exp10_set_exceptional(double x) {
if (xbits.is_nan())
return x;
#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
if (fputil::quick_get_round() == FE_UPWARD)
return FPBits::min_subnormal().get_val();
#endif
fputil::set_errno_if_required(ERANGE);
fputil::raise_except_if_required(FE_UNDERFLOW);
return 0.0;
@ -272,9 +274,11 @@ LIBC_INLINE double exp10_set_exceptional(double x) {
// x >= log10(2^1024) or +inf/nan
// x is finite
if (x_u < 0x7ff0'0000'0000'0000ULL) {
#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
int rounding = fputil::quick_get_round();
if (rounding == FE_DOWNWARD || rounding == FE_TOWARDZERO)
return FPBits::max_normal().get_val();
#endif
fputil::set_errno_if_required(ERANGE);
fputil::raise_except_if_required(FE_OVERFLOW);

View file

@ -37,8 +37,10 @@ LIBC_INLINE float exp10f(float x) {
// exp(nan) = nan
if (xbits.is_nan())
return x;
#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
if (fputil::fenv_is_round_up())
return FPBits::min_subnormal().get_val();
#endif
fputil::set_errno_if_required(ERANGE);
fputil::raise_except_if_required(FE_UNDERFLOW);
return 0.0f;
@ -47,9 +49,11 @@ LIBC_INLINE float exp10f(float x) {
if (xbits.is_pos() && (x_u >= 0x421a'209bU)) {
// x is finite
if (x_u < 0x7f80'0000U) {
#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
int rounding = fputil::quick_get_round();
if (rounding == FE_DOWNWARD || rounding == FE_TOWARDZERO)
return FPBits::max_normal().get_val();
#endif
fputil::set_errno_if_required(ERANGE);
fputil::raise_except_if_required(FE_OVERFLOW);
@ -62,8 +66,12 @@ LIBC_INLINE float exp10f(float x) {
// When |x| <= log10(2)*2^-6
if (LIBC_UNLIKELY(x_abs <= 0x3b9a'209bU)) {
if (LIBC_UNLIKELY(x_u == 0xb25e'5bd9U)) { // x = -0x1.bcb7b2p-27f
#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
return 0x1.fffffep-1f;
#else
if (fputil::fenv_is_round_to_nearest())
return 0x1.fffffep-1f;
#endif
}
// |x| < 2^-25
// 10^x ~ 1 + log(10) * x
@ -76,8 +84,10 @@ LIBC_INLINE float exp10f(float x) {
// Exceptional value.
if (LIBC_UNLIKELY(x_u == 0x3d14'd956U)) { // x = 0x1.29b2acp-5f
#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
if (fputil::fenv_is_round_up())
return 0x1.1657c4p+0f;
#endif
}
// Exact outputs when x = 1, 2, ..., 10.

View file

@ -82,6 +82,11 @@ LIBC_INLINE constexpr float16 exp10f16(float16 x) {
if (x_bits.is_inf())
return FPBits::inf().get_val();
#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
fputil::set_errno_if_required(ERANGE);
fputil::raise_except_if_required(FE_OVERFLOW);
return FPBits::inf().get_val();
#else
switch (fputil::quick_get_round()) {
case FE_TONEAREST:
case FE_UPWARD:
@ -91,6 +96,7 @@ LIBC_INLINE constexpr float16 exp10f16(float16 x) {
default:
return FPBits::max_normal().get_val();
}
#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
}
// When x <= -8.
@ -102,8 +108,10 @@ LIBC_INLINE constexpr float16 exp10f16(float16 x) {
fputil::set_errno_if_required(ERANGE);
fputil::raise_except_if_required(FE_UNDERFLOW | FE_INEXACT);
#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
if (fputil::fenv_is_round_up())
return FPBits::min_subnormal().get_val();
#endif
return FPBits::zero().get_val();
}
}

View file

@ -115,9 +115,11 @@ LIBC_INLINE float exp10m1f(float x) {
// When x >= log10(2^128), or x is nan
if (LIBC_UNLIKELY(xbits.is_pos() && x_u >= 0x421a'209bU)) {
if (xbits.is_finite()) {
#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
int rounding = fputil::quick_get_round();
if (rounding == FE_DOWNWARD || rounding == FE_TOWARDZERO)
return FPBits::max_normal().get_val();
#endif
fputil::set_errno_if_required(ERANGE);
fputil::raise_except_if_required(FE_OVERFLOW);
@ -164,10 +166,15 @@ LIBC_INLINE float exp10m1f(float x) {
if (xbits.is_nan())
return x;
#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
if (x_u == 0xc0f0d2f1) // x = log10(2^-25)
return -0x1.ffff'fep-1f; // -1.0f + 0x1.0p-24f
#else
int rounding = fputil::quick_get_round();
if (rounding == FE_UPWARD || rounding == FE_TOWARDZERO ||
(rounding == FE_TONEAREST && x_u == 0xc0f0d2f1))
return -0x1.ffff'fep-1f; // -1.0f + 0x1.0p-24f
#endif
fputil::set_errno_if_required(ERANGE);
fputil::raise_except_if_required(FE_UNDERFLOW);
@ -193,21 +200,33 @@ LIBC_INLINE float exp10m1f(float x) {
case 0x40e00000U: // x = 7.0f
return 9'999'999.0f;
case 0x41000000U: { // x = 8.0f
#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
return 100'000'000.0f;
#else
int rounding = fputil::quick_get_round();
if (rounding == FE_UPWARD || rounding == FE_TONEAREST)
return 100'000'000.0f;
#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
return 99'999'992.0f;
}
case 0x41100000U: { // x = 9.0f
#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
return 1'000'000'000.0f;
#else
int rounding = fputil::quick_get_round();
if (rounding == FE_UPWARD || rounding == FE_TONEAREST)
return 1'000'000'000.0f;
#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
return 999'999'936.0f;
}
case 0x41200000U: { // x = 10.0f
#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
return 10'000'000'000.0f;
#else
int rounding = fputil::quick_get_round();
if (rounding == FE_UPWARD || rounding == FE_TONEAREST)
return 10'000'000'000.0f;
#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
return 9'999'998'976.0f;
}
}

View file

@ -93,6 +93,11 @@ LIBC_INLINE constexpr float16 exp10m1f16(float16 x) {
if (x_bits.is_inf())
return FPBits::inf().get_val();
#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
fputil::set_errno_if_required(ERANGE);
fputil::raise_except_if_required(FE_OVERFLOW | FE_INEXACT);
return FPBits::inf().get_val();
#else
switch (fputil::quick_get_round()) {
case FE_TONEAREST:
case FE_UPWARD:
@ -102,6 +107,7 @@ LIBC_INLINE constexpr float16 exp10m1f16(float16 x) {
default:
return FPBits::max_normal().get_val();
}
#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
}
// When x < -11 * log10(2).
@ -117,13 +123,20 @@ LIBC_INLINE constexpr float16 exp10m1f16(float16 x) {
}
// When x < -0x1.ce4p+1, round(10^x - 1, HP, RN) = -1.
#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
fputil::raise_except_if_required(FE_INEXACT);
return FPBits::one(Sign::NEG).get_val();
#else
switch (fputil::quick_get_round()) {
case FE_TONEAREST:
case FE_DOWNWARD:
fputil::raise_except_if_required(FE_INEXACT);
return FPBits::one(Sign::NEG).get_val();
default:
fputil::raise_except_if_required(FE_INEXACT);
return fputil::cast<float16>(-0x1.ffcp-1);
}
#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
}
// When |x| <= 2^(-3).
@ -150,8 +163,8 @@ LIBC_INLINE constexpr float16 exp10m1f16(float16 x) {
}
// When x is 1, 2, or 3. These are hard-to-round cases with exact results.
// 10^4 - 1 = 9'999 is not exactly representable as a float16, but luckily the
// polynomial approximation gives the correct result for x = 4 in all
// 10^4 - 1 = 9'999 is not exactly representable as a float16, but luckily
// the polynomial approximation gives the correct result for x = 4 in all
// rounding modes.
if (LIBC_UNLIKELY((x_u & ~(0x3c00U | 0x4000U | 0x4200U | 0x4400U)) == 0)) {
switch (x_u) {

View file

@ -238,8 +238,10 @@ LIBC_INLINE double set_exceptional(double x) {
if (xbits.is_nan())
return x;
#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
if (fputil::quick_get_round() == FE_UPWARD)
return FPBits::min_subnormal().get_val();
#endif
fputil::set_errno_if_required(ERANGE);
fputil::raise_except_if_required(FE_UNDERFLOW);
return 0.0;
@ -251,9 +253,11 @@ LIBC_INLINE double set_exceptional(double x) {
// x >= 1024 or +inf/nan
// x is finite
if (x_u < 0x7ff0'0000'0000'0000ULL) {
#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
int rounding = fputil::quick_get_round();
if (rounding == FE_DOWNWARD || rounding == FE_TOWARDZERO)
return FPBits::max_normal().get_val();
#endif
fputil::set_errno_if_required(ERANGE);
fputil::raise_except_if_required(FE_OVERFLOW);

View file

@ -76,9 +76,11 @@ LIBC_INLINE float exp2f(float x) {
if (xbits.is_pos()) {
// x is finite
if (x_u < 0x7f80'0000U) {
#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
int rounding = fputil::quick_get_round();
if (rounding == FE_DOWNWARD || rounding == FE_TOWARDZERO)
return FPBits::max_normal().get_val();
#endif
fputil::set_errno_if_required(ERANGE);
fputil::raise_except_if_required(FE_OVERFLOW);
@ -94,8 +96,10 @@ LIBC_INLINE float exp2f(float x) {
// exp(nan) = nan
if (xbits.is_nan())
return x;
#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
if (fputil::fenv_is_round_up())
return FPBits::min_subnormal().get_val();
#endif
if (x != 0.0f) {
fputil::set_errno_if_required(ERANGE);
fputil::raise_except_if_required(FE_UNDERFLOW);

View file

@ -66,6 +66,11 @@ LIBC_INLINE constexpr float16 exp2f16(float16 x) {
if (x_bits.is_inf())
return FPBits::inf().get_val();
#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
fputil::set_errno_if_required(ERANGE);
fputil::raise_except_if_required(FE_OVERFLOW);
return FPBits::inf().get_val();
#else
switch (fputil::quick_get_round()) {
case FE_TONEAREST:
case FE_UPWARD:
@ -75,6 +80,7 @@ LIBC_INLINE constexpr float16 exp2f16(float16 x) {
default:
return FPBits::max_normal().get_val();
}
#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
}
// When x <= -25.
@ -86,8 +92,10 @@ LIBC_INLINE constexpr float16 exp2f16(float16 x) {
fputil::set_errno_if_required(ERANGE);
fputil::raise_except_if_required(FE_UNDERFLOW | FE_INEXACT);
#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
if (fputil::fenv_is_round_up())
return FPBits::min_subnormal().get_val();
#endif
return FPBits::zero().get_val();
}
}

View file

@ -96,9 +96,11 @@ LIBC_INLINE float exp2m1f(float x) {
// x >= 128, or x is nan
if (xbits.is_pos()) {
if (xbits.is_finite()) {
#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
int rounding = fputil::quick_get_round();
if (rounding == FE_DOWNWARD || rounding == FE_TOWARDZERO)
return FPBits::max_normal().get_val();
#endif
fputil::set_errno_if_required(ERANGE);
fputil::raise_except_if_required(FE_OVERFLOW);
@ -117,9 +119,11 @@ LIBC_INLINE float exp2m1f(float x) {
if (xbits.is_nan())
return x;
#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
int rounding = fputil::quick_get_round();
if (rounding == FE_UPWARD || rounding == FE_TOWARDZERO)
return -0x1.ffff'fep-1f; // -1.0f + 0x1.0p-24f
#endif
fputil::set_errno_if_required(ERANGE);
fputil::raise_except_if_required(FE_UNDERFLOW);

View file

@ -106,6 +106,11 @@ LIBC_INLINE constexpr float16 exp2m1f16(float16 x) {
if (x_bits.is_inf())
return FPBits::inf().get_val();
#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
fputil::set_errno_if_required(ERANGE);
fputil::raise_except_if_required(FE_OVERFLOW | FE_INEXACT);
return FPBits::inf().get_val();
#else
switch (fputil::quick_get_round()) {
case FE_TONEAREST:
case FE_UPWARD:
@ -115,6 +120,7 @@ LIBC_INLINE constexpr float16 exp2m1f16(float16 x) {
default:
return FPBits::max_normal().get_val();
}
#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
}
// When x < -11.
@ -129,13 +135,20 @@ LIBC_INLINE constexpr float16 exp2m1f16(float16 x) {
fputil::cast<float16>(-0x1.ffcp-1));
// When x <= -12, round(2^x - 1, HP, RN) = -1.
#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
fputil::raise_except_if_required(FE_INEXACT);
return FPBits::one(Sign::NEG).get_val();
#else
switch (fputil::quick_get_round()) {
case FE_TONEAREST:
case FE_DOWNWARD:
fputil::raise_except_if_required(FE_INEXACT);
return FPBits::one(Sign::NEG).get_val();
default:
fputil::raise_except_if_required(FE_INEXACT);
return fputil::cast<float16>(-0x1.ffcp-1);
}
#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
}
// When |x| <= 2^(-3).

View file

@ -53,8 +53,10 @@ LIBC_INLINE float expf(float x) {
// exp(nan) = nan
if (xbits.is_nan())
return x;
#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
if (fputil::fenv_is_round_up())
return FPBits::min_subnormal().get_val();
#endif
fputil::set_errno_if_required(ERANGE);
fputil::raise_except_if_required(FE_UNDERFLOW);
return 0.0f;
@ -63,9 +65,11 @@ LIBC_INLINE float expf(float x) {
if (xbits.is_pos() && (xbits.uintval() >= 0x42b2'0000)) {
// x is finite
if (xbits.uintval() < 0x7f80'0000U) {
#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
int rounding = fputil::quick_get_round();
if (rounding == FE_DOWNWARD || rounding == FE_TOWARDZERO)
return FPBits::max_normal().get_val();
#endif
fputil::set_errno_if_required(ERANGE);
fputil::raise_except_if_required(FE_OVERFLOW);

View file

@ -75,6 +75,11 @@ LIBC_INLINE constexpr float16 expf16(float16 x) {
if (x_bits.is_inf())
return FPBits::inf().get_val();
#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
fputil::set_errno_if_required(ERANGE);
fputil::raise_except_if_required(FE_OVERFLOW);
return FPBits::inf().get_val();
#else
switch (fputil::quick_get_round()) {
case FE_TONEAREST:
case FE_UPWARD:
@ -84,6 +89,7 @@ LIBC_INLINE constexpr float16 expf16(float16 x) {
default:
return FPBits::max_normal().get_val();
}
#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
}
// When x <= -18.
@ -95,12 +101,16 @@ LIBC_INLINE constexpr float16 expf16(float16 x) {
fputil::set_errno_if_required(ERANGE);
fputil::raise_except_if_required(FE_UNDERFLOW | FE_INEXACT);
#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
return FPBits::zero().get_val();
#else
switch (fputil::quick_get_round()) {
case FE_UPWARD:
return FPBits::min_subnormal().get_val();
default:
return FPBits::zero().get_val();
}
#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
}
// When 0 < |x| <= 2^(-5).

View file

@ -265,9 +265,11 @@ LIBC_INLINE double set_exceptional(double x) {
// x >= round(log(MAX_NORMAL), D, RU) = 0x1.62e42fefa39fp+9 or +inf/nan
// x is finite
if (x_u < 0x7ff0'0000'0000'0000ULL) {
#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
int rounding = fputil::quick_get_round();
if (rounding == FE_DOWNWARD || rounding == FE_TOWARDZERO)
return FPBits::max_normal().get_val();
#endif
fputil::set_errno_if_required(ERANGE);
fputil::raise_except_if_required(FE_OVERFLOW);

View file

@ -38,17 +38,25 @@ LIBC_INLINE float expm1f(float x) {
#ifndef LIBC_MATH_HAS_SKIP_ACCURATE_PASS
// Exceptional value
if (LIBC_UNLIKELY(x_u == 0x3e35'bec5U)) { // x = 0x1.6b7d8ap-3f
#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
return 0x1.8dbe64p-3f;
#else
int round_mode = fputil::quick_get_round();
if (round_mode == FE_TONEAREST || round_mode == FE_UPWARD)
return 0x1.8dbe64p-3f;
#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
return 0x1.8dbe62p-3f;
}
#if !defined(LIBC_TARGET_CPU_HAS_FMA_DOUBLE)
if (LIBC_UNLIKELY(x_u == 0xbdc1'c6cbU)) { // x = -0x1.838d96p-4f
#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
return -0x1.71c884p-4f;
#else
int round_mode = fputil::quick_get_round();
if (round_mode == FE_TONEAREST || round_mode == FE_DOWNWARD)
return -0x1.71c884p-4f;
return -0x1.71c882p-4f;
#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
}
#endif // LIBC_TARGET_CPU_HAS_FMA_DOUBLE
#endif // !LIBC_MATH_HAS_SKIP_ACCURATE_PASS
@ -63,17 +71,21 @@ LIBC_INLINE float expm1f(float x) {
// exp(nan) = nan
if (xbits.is_nan())
return x;
#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
int round_mode = fputil::quick_get_round();
if (round_mode == FE_UPWARD || round_mode == FE_TOWARDZERO)
return -0x1.ffff'fep-1f; // -1.0f + 0x1.0p-24f
#endif
return -1.0f;
} else {
// x >= 89 or nan
if (xbits.uintval() >= 0x42b2'0000) {
if (xbits.uintval() < 0x7f80'0000U) {
#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
int rounding = fputil::quick_get_round();
if (rounding == FE_DOWNWARD || rounding == FE_TOWARDZERO)
return FPBits::max_normal().get_val();
#endif
fputil::set_errno_if_required(ERANGE);
fputil::raise_except_if_required(FE_OVERFLOW);

View file

@ -86,6 +86,11 @@ LIBC_INLINE constexpr float16 expm1f16(float16 x) {
if (x_bits.is_inf())
return FPBits::inf().get_val();
#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
fputil::set_errno_if_required(ERANGE);
fputil::raise_except_if_required(FE_OVERFLOW | FE_INEXACT);
return FPBits::inf().get_val();
#else
switch (fputil::quick_get_round()) {
case FE_TONEAREST:
case FE_UPWARD:
@ -95,6 +100,7 @@ LIBC_INLINE constexpr float16 expm1f16(float16 x) {
default:
return FPBits::max_normal().get_val();
}
#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
}
// When x <= -11 * log(2).

View file

@ -94,11 +94,13 @@ LIBC_INLINE double sin(double x) {
return fputil::multiply_add(x, -0x1.0p-54, x);
#else
if (LIBC_UNLIKELY(x_e < 4)) {
#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
int rounding_mode = fputil::quick_get_round();
if (rounding_mode == FE_TOWARDZERO ||
(xbits.sign() == Sign::POS && rounding_mode == FE_DOWNWARD) ||
(xbits.sign() == Sign::NEG && rounding_mode == FE_UPWARD))
return FPBits(xbits.uintval() - 1).get_val();
#endif // !LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
}
return fputil::multiply_add(x, -0x1.0p-54, x);
#endif // LIBC_TARGET_CPU_HAS_FMA_DOUBLE

View file

@ -67,11 +67,13 @@ LIBC_INLINE void sincos(double x, double *sin_x, double *cos_x) {
*cos_x = fputil::round_result_slightly_down(1.0);
if (LIBC_UNLIKELY(x_e < 4)) {
#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
int rounding_mode = fputil::quick_get_round();
if (rounding_mode == FE_TOWARDZERO ||
(xbits.sign() == Sign::POS && rounding_mode == FE_DOWNWARD) ||
(xbits.sign() == Sign::NEG && rounding_mode == FE_UPWARD))
*sin_x = FPBits(xbits.uintval() - 1).get_val();
#endif // !LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
}
*sin_x = fputil::multiply_add(x, -0x1.0p-54, x);
#endif // LIBC_TARGET_CPU_HAS_FMA_DOUBLE

View file

@ -176,6 +176,10 @@ LIBC_INLINE void sincosf(float x, float *sinp, float *cosp) {
uint32_t s = EXCEPT_OUTPUTS_SIN[i][0]; // FE_TOWARDZERO
uint32_t c = EXCEPT_OUTPUTS_COS[i][0]; // FE_TOWARDZERO
bool x_sign = x < 0;
#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
s += EXCEPT_OUTPUTS_SIN[i][3];
c += EXCEPT_OUTPUTS_COS[i][3];
#else
switch (fputil::quick_get_round()) {
case FE_UPWARD:
s += x_sign ? EXCEPT_OUTPUTS_SIN[i][2] : EXCEPT_OUTPUTS_SIN[i][1];
@ -190,6 +194,7 @@ LIBC_INLINE void sincosf(float x, float *sinp, float *cosp) {
c += EXCEPT_OUTPUTS_COS[i][3];
break;
}
#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
*sinp = x_sign ? -FPBits(s).get_val() : FPBits(s).get_val();
*cosp = FPBits(c).get_val();

View file

@ -152,10 +152,12 @@ LIBC_INLINE float sinf(float x) {
#ifndef LIBC_MATH_HAS_SKIP_ACCURATE_PASS
if (LIBC_UNLIKELY(x_abs == 0x4619'9998U)) { // x = 0x1.33333p13
float r = -0x1.63f4bap-2f;
#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
int rounding = fputil::quick_get_round();
if ((rounding == FE_DOWNWARD && xbits.is_pos()) ||
(rounding == FE_UPWARD && xbits.is_neg()))
r = -0x1.63f4bcp-2f;
#endif // !LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
return xbits.is_neg() ? -r : r;
}
#endif // !LIBC_MATH_HAS_SKIP_ACCURATE_PASS

View file

@ -79,7 +79,9 @@ LIBC_INLINE float16 sinf16(float16 x) {
return r.value();
#endif // !LIBC_MATH_HAS_SKIP_ACCURATE_PASS
#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
int rounding = fputil::quick_get_round();
#endif
// Exhaustive tests show that for |x| <= 0x1.f4p-11, 1ULP rounding errors
// occur. To fix this, the following apply:
@ -88,6 +90,7 @@ LIBC_INLINE float16 sinf16(float16 x) {
if (LIBC_UNLIKELY(x_abs == 0U))
return x;
#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
// When x > 0, and rounding upward, sin(x) == x.
// When x < 0, and rounding downward, sin(x) == x.
if ((rounding == FE_UPWARD && xbits.is_pos()) ||
@ -99,6 +102,10 @@ LIBC_INLINE float16 sinf16(float16 x) {
x_u--;
return FPBits(x_u).get_val();
}
#endif // !LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
// TODO: what about the case with rounding nearest and we're in here?
// Previously, it's an UB
}
if (xbits.is_inf_or_nan()) {

View file

@ -32,8 +32,12 @@ LIBC_INLINE float sinhf(float x) {
#ifndef LIBC_MATH_HAS_SKIP_ACCURATE_PASS
// |x| = 0.0005589424981735646724700927734375
if (LIBC_UNLIKELY(x_abs == 0x3a12'85ffU)) {
#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
return x;
#else
if (fputil::fenv_is_round_to_nearest())
return x;
#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
}
#endif // !LIBC_MATH_HAS_SKIP_ACCURATE_PASS
@ -61,6 +65,7 @@ LIBC_INLINE float sinhf(float x) {
if (xbits.is_inf())
return x;
#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
int rounding = fputil::quick_get_round();
if (xbits.is_neg()) {
if (LIBC_UNLIKELY(rounding == FE_UPWARD || rounding == FE_TOWARDZERO))
@ -69,6 +74,7 @@ LIBC_INLINE float sinhf(float x) {
if (LIBC_UNLIKELY(rounding == FE_DOWNWARD || rounding == FE_TOWARDZERO))
return FPBits::max_normal().get_val();
}
#endif // !LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
fputil::set_errno_if_required(ERANGE);
fputil::raise_except_if_required(FE_OVERFLOW);

View file

@ -129,6 +129,11 @@ LIBC_INLINE constexpr float16 sinhf16(float16 x) {
if (x_bits.is_inf())
return FPBits::inf(x_bits.sign()).get_val();
#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
fputil::set_errno_if_required(ERANGE);
fputil::raise_except_if_required(FE_OVERFLOW | FE_INEXACT);
return FPBits::inf(x_bits.sign()).get_val();
#else
int rounding_mode = fputil::quick_get_round();
if (rounding_mode == FE_TONEAREST ||
(x_bits.is_pos() && rounding_mode == FE_UPWARD) ||
@ -137,12 +142,15 @@ LIBC_INLINE constexpr float16 sinhf16(float16 x) {
fputil::raise_except_if_required(FE_OVERFLOW | FE_INEXACT);
return FPBits::inf(x_bits.sign()).get_val();
}
#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
return FPBits::max_normal(x_bits.sign()).get_val();
}
// When -2^(-14) <= x <= -2^(-9).
#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
if (fputil::fenv_is_round_down())
return FPBits(static_cast<uint16_t>(x_u + 1)).get_val();
#endif
return FPBits(static_cast<uint16_t>(x_u)).get_val();
}

View file

@ -149,10 +149,13 @@ LIBC_INLINE double tan(double x) {
return fputil::multiply_add(x, 0x1.0p-54, x);
#else
if (LIBC_UNLIKELY(x_e < 4)) {
// TODO: UB for rounding nearest
#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
int rounding_mode = fputil::quick_get_round();
if ((xbits.sign() == Sign::POS && rounding_mode == FE_UPWARD) ||
(xbits.sign() == Sign::NEG && rounding_mode == FE_DOWNWARD))
return FPBits(xbits.uintval() + 1).get_val();
#endif // !LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
}
return fputil::multiply_add(x, 0x1.0p-54, x);
#endif // LIBC_TARGET_CPU_HAS_FMA_DOUBLE

View file

@ -68,6 +68,7 @@ LIBC_INLINE float16 tanf16(float16 x) {
if (LIBC_UNLIKELY(x_abs == 0))
return x;
#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
int rounding = fputil::quick_get_round();
// Exhaustive tests show that, when:
@ -77,6 +78,7 @@ LIBC_INLINE float16 tanf16(float16 x) {
if ((xbits.is_pos() && rounding == FE_UPWARD) ||
(xbits.is_neg() && rounding == FE_DOWNWARD))
return fputil::cast<float16>(fputil::multiply_add(xf, 0x1.0p-11f, xf));
#endif // !LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
return x;
}

View file

@ -64,6 +64,9 @@ LIBC_INLINE float16 tanhf16(float16 x) {
// When -2^(-14) <= x <= -2^(-9).
if (x_u >= 0x8400U && x_u <= 0x9800U) {
#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
return x;
#else
switch (fputil::quick_get_round()) {
case FE_TONEAREST:
case FE_DOWNWARD:
@ -71,6 +74,7 @@ LIBC_INLINE float16 tanhf16(float16 x) {
default:
return FPBits(static_cast<uint16_t>(x_u - 1U)).get_val();
}
#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
}
// When |x| <= 0x1.d2p-4.
@ -98,12 +102,18 @@ LIBC_INLINE float16 tanhf16(float16 x) {
// When |x| >= atanh(1 - 2^(-11)).
fputil::raise_except_if_required(FE_INEXACT);
#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
if (x_abs >= 0x4482U) {
return FPBits::one(x_bits.sign()).get_val();
}
#else
int rounding_mode = fputil::quick_get_round();
if ((rounding_mode == FE_TONEAREST && x_abs >= 0x4482U) ||
(rounding_mode == FE_UPWARD && x_bits.is_pos()) ||
(rounding_mode == FE_DOWNWARD && x_bits.is_neg())) {
return FPBits::one(x_bits.sign()).get_val();
}
#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
if (x_bits.is_pos())
return fputil::cast<float16>(0x1.ffcp-1);
return fputil::cast<float16>(-0x1.ffcp-1);

View file

@ -1129,7 +1129,9 @@ strtofloatingpoint(const CharType *__restrict src) {
size_t index = first_non_whitespace(src);
int sign = get_sign(src + index);
#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
bool is_positive = (sign >= 0);
#endif
index += (sign != 0);
if (sign < 0) {
@ -1146,6 +1148,7 @@ strtofloatingpoint(const CharType *__restrict src) {
}
RoundDirection round_direction = RoundDirection::Nearest;
#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
switch (fputil::quick_get_round()) {
case FE_TONEAREST:
round_direction = RoundDirection::Nearest;
@ -1160,6 +1163,7 @@ strtofloatingpoint(const CharType *__restrict src) {
round_direction = RoundDirection::Down;
break;
}
#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
StrToNumResult<ExpandedFloat<T>> parse_result({0, 0});
if (base == 16) {

View file

@ -49,6 +49,14 @@ constexpr uint32_t MAX_BLOCK = 999999999;
LIBC_INLINE RoundDirection get_round_direction(int last_digit, bool truncated,
Sign sign) {
#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
if (last_digit != 5) {
return last_digit > 5 ? RoundDirection::Up : RoundDirection::Down;
} else {
return !truncated ? RoundDirection::Even : RoundDirection::Up;
}
#endif
switch (fputil::quick_get_round()) {
case FE_TONEAREST:
// Round to nearest, if it's exactly halfway then round to even.

View file

@ -112,6 +112,13 @@ LIBC_INLINE int convert_float_hex_exp(Writer<write_mode> *writer,
mantissa >>= shift_amount;
#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
// Round to nearest, if it's exactly halfway then round to even.
if (truncated_bits > halfway_const)
++mantissa;
else if (truncated_bits == halfway_const)
mantissa = mantissa + (mantissa & 1);
#else
switch (fputil::quick_get_round()) {
case FE_TONEAREST:
// Round to nearest, if it's exactly halfway then round to even.
@ -131,6 +138,7 @@ LIBC_INLINE int convert_float_hex_exp(Writer<write_mode> *writer,
case FE_TOWARDZERO:
break;
}
#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
// If the rounding caused an overflow, shift the mantissa and adjust the
// exponent to match.

View file

@ -196,6 +196,12 @@ template <typename T> struct FPTest : public ErrnoCheckingTest {
static constexpr T min_denormal = FPBits::min_subnormal().get_val();
static constexpr T max_denormal = FPBits::max_subnormal().get_val();
#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
static constexpr int N_ROUNDING_MODES = 1;
static constexpr fputil::testing::RoundingMode ROUNDING_MODES[1] = {
fputil::testing::RoundingMode::Nearest,
};
#else
static constexpr int N_ROUNDING_MODES = 4;
static constexpr fputil::testing::RoundingMode ROUNDING_MODES[4] = {
fputil::testing::RoundingMode::Nearest,
@ -203,6 +209,7 @@ template <typename T> struct FPTest : public ErrnoCheckingTest {
fputil::testing::RoundingMode::Downward,
fputil::testing::RoundingMode::TowardZero,
};
#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
void TearDown() override {
// TODO (PR 135320): Remove this override once all FPTest instances are
@ -395,6 +402,20 @@ private:
#define EXPECT_FP_EQ_ROUNDING_NEAREST(expected, actual) \
EXPECT_FP_EQ_ROUNDING_MODE((expected), (actual), RoundingMode::Nearest)
#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
#define EXPECT_FP_EQ_ROUNDING_UPWARD(expected, actual) \
do { \
} while (0)
#define EXPECT_FP_EQ_ROUNDING_DOWNWARD(expected, actual) \
do { \
} while (0)
#define EXPECT_FP_EQ_ROUNDING_TOWARD_ZERO(expected, actual) \
do { \
} while (0)
#else // !LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
#define EXPECT_FP_EQ_ROUNDING_UPWARD(expected, actual) \
EXPECT_FP_EQ_ROUNDING_MODE((expected), (actual), RoundingMode::Upward)
@ -404,6 +425,8 @@ private:
#define EXPECT_FP_EQ_ROUNDING_TOWARD_ZERO(expected, actual) \
EXPECT_FP_EQ_ROUNDING_MODE((expected), (actual), RoundingMode::TowardZero)
#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
#define EXPECT_FP_EQ_ALL_ROUNDING_1(expected, actual) \
do { \
EXPECT_FP_EQ_ROUNDING_NEAREST((expected), (actual)); \
@ -450,6 +473,19 @@ private:
#define ASSERT_FP_EQ_ROUNDING_NEAREST(expected, actual) \
ASSERT_FP_EQ_ROUNDING_MODE((expected), (actual), RoundingMode::Nearest)
#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
#define ASSERT_FP_EQ_ROUNDING_UPWARD(expected, actual) \
do { \
} while (0)
#define ASSERT_FP_EQ_ROUNDING_DOWNWARD(expected, actual) \
do { \
} while (0)
#define ASSERT_FP_EQ_ROUNDING_TOWARD_ZERO(expected, actual) \
do { \
} while (0)
#else // !LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
#define ASSERT_FP_EQ_ROUNDING_UPWARD(expected, actual) \
ASSERT_FP_EQ_ROUNDING_MODE((expected), (actual), RoundingMode::Upward)
@ -459,6 +495,8 @@ private:
#define ASSERT_FP_EQ_ROUNDING_TOWARD_ZERO(expected, actual) \
ASSERT_FP_EQ_ROUNDING_MODE((expected), (actual), RoundingMode::TowardZero)
#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
#define EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_MODE( \
expected, actual, expected_except, rounding_mode) \
do { \
@ -476,6 +514,127 @@ private:
EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_MODE( \
(expected), (actual), (expected_except), RoundingMode::Nearest)
#define ASSERT_FP_EQ_WITH_EXCEPTION_ROUNDING_MODE( \
expected, actual, expected_except, rounding_mode) \
do { \
using namespace LIBC_NAMESPACE::fputil::testing; \
ForceRoundingMode __r((rounding_mode)); \
if (__r.success) { \
LIBC_NAMESPACE::fputil::clear_except(static_cast<int>(FE_ALL_EXCEPT)); \
ASSERT_FP_EQ((expected), (actual)); \
ASSERT_FP_EXCEPTION(expected_except); \
} \
} while (0)
#define ASSERT_FP_EQ_ALL_ROUNDING_1(expected, actual) \
do { \
ASSERT_FP_EQ_ROUNDING_NEAREST((expected), (actual)); \
ASSERT_FP_EQ_ROUNDING_UPWARD((expected), (actual)); \
ASSERT_FP_EQ_ROUNDING_DOWNWARD((expected), (actual)); \
ASSERT_FP_EQ_ROUNDING_TOWARD_ZERO((expected), (actual)); \
} while (0)
#define ASSERT_FP_EQ_ALL_ROUNDING_4(expected_nearest, expected_upward, \
expected_downward, expected_toward_zero, \
actual) \
do { \
ASSERT_FP_EQ_ROUNDING_NEAREST((expected_nearest), (actual)); \
ASSERT_FP_EQ_ROUNDING_UPWARD((expected_upward), (actual)); \
ASSERT_FP_EQ_ROUNDING_DOWNWARD((expected_downward), (actual)); \
ASSERT_FP_EQ_ROUNDING_TOWARD_ZERO((expected_toward_zero), (actual)); \
} while (0)
#define ASSERT_FP_EQ_ALL_ROUNDING_UNSUPPORTED(...) \
static_assert(false, "Unsupported number of arguments")
#define ASSERT_FP_EQ_ALL_ROUNDING_GET_6TH_ARG(ARG1, ARG2, ARG3, ARG4, ARG5, \
ARG6, ...) \
ARG6
#define ASSERT_FP_EQ_ALL_ROUNDING_SELECTION(...) \
ASSERT_FP_EQ_ALL_ROUNDING_GET_6TH_ARG( \
__VA_ARGS__, ASSERT_FP_EQ_ALL_ROUNDING_4, \
ASSERT_FP_EQ_ALL_ROUNDING_UNSUPPORTED, \
ASSERT_FP_EQ_ALL_ROUNDING_UNSUPPORTED, ASSERT_FP_EQ_ALL_ROUNDING_1)
#define ASSERT_FP_EQ_ALL_ROUNDING(...) \
ASSERT_FP_EQ_ALL_ROUNDING_SELECTION(__VA_ARGS__)(__VA_ARGS__)
#define ASSERT_FP_EQ_WITH_EXCEPTION_ROUNDING_NEAREST(expected, actual, \
expected_except) \
ASSERT_FP_EQ_WITH_EXCEPTION_ROUNDING_MODE( \
(expected), (actual), (expected_except), RoundingMode::Nearest)
#define ASSERT_FP_EQ_WITH_EXCEPTION_ROUNDING_MODE( \
expected, actual, expected_except, rounding_mode) \
do { \
using namespace LIBC_NAMESPACE::fputil::testing; \
ForceRoundingMode __r((rounding_mode)); \
if (__r.success) { \
LIBC_NAMESPACE::fputil::clear_except(static_cast<int>(FE_ALL_EXCEPT)); \
ASSERT_FP_EQ((expected), (actual)); \
ASSERT_FP_EXCEPTION(expected_except); \
} \
} while (0)
#define ASSERT_FP_EQ_ALL_ROUNDING_1(expected, actual) \
do { \
ASSERT_FP_EQ_ROUNDING_NEAREST((expected), (actual)); \
ASSERT_FP_EQ_ROUNDING_UPWARD((expected), (actual)); \
ASSERT_FP_EQ_ROUNDING_DOWNWARD((expected), (actual)); \
ASSERT_FP_EQ_ROUNDING_TOWARD_ZERO((expected), (actual)); \
} while (0)
#define ASSERT_FP_EQ_ALL_ROUNDING_4(expected_nearest, expected_upward, \
expected_downward, expected_toward_zero, \
actual) \
do { \
ASSERT_FP_EQ_ROUNDING_NEAREST((expected_nearest), (actual)); \
ASSERT_FP_EQ_ROUNDING_UPWARD((expected_upward), (actual)); \
ASSERT_FP_EQ_ROUNDING_DOWNWARD((expected_downward), (actual)); \
ASSERT_FP_EQ_ROUNDING_TOWARD_ZERO((expected_toward_zero), (actual)); \
} while (0)
#define ASSERT_FP_EQ_ALL_ROUNDING_UNSUPPORTED(...) \
static_assert(false, "Unsupported number of arguments")
#define ASSERT_FP_EQ_ALL_ROUNDING_GET_6TH_ARG(ARG1, ARG2, ARG3, ARG4, ARG5, \
ARG6, ...) \
ARG6
#define ASSERT_FP_EQ_ALL_ROUNDING_SELECTION(...) \
ASSERT_FP_EQ_ALL_ROUNDING_GET_6TH_ARG( \
__VA_ARGS__, ASSERT_FP_EQ_ALL_ROUNDING_4, \
ASSERT_FP_EQ_ALL_ROUNDING_UNSUPPORTED, \
ASSERT_FP_EQ_ALL_ROUNDING_UNSUPPORTED, ASSERT_FP_EQ_ALL_ROUNDING_1)
#define ASSERT_FP_EQ_ALL_ROUNDING(...) \
ASSERT_FP_EQ_ALL_ROUNDING_SELECTION(__VA_ARGS__)(__VA_ARGS__)
#define ASSERT_FP_EQ_WITH_EXCEPTION_ROUNDING_NEAREST(expected, actual, \
expected_except) \
ASSERT_FP_EQ_WITH_EXCEPTION_ROUNDING_MODE( \
(expected), (actual), (expected_except), RoundingMode::Nearest)
#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
#define EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_UPWARD(expected, actual, \
expected_except) \
do { \
} while (0)
#define EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_DOWNWARD(expected, actual, \
expected_except) \
do { \
} while (0)
#define EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_TOWARD_ZERO(expected, actual, \
expected_except) \
do { \
} while (0)
#else // !LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
#define EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_UPWARD(expected, actual, \
expected_except) \
EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_MODE( \
@ -491,6 +650,8 @@ private:
EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_MODE( \
(expected), (actual), (expected_except), RoundingMode::TowardZero)
#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
#define EXPECT_FP_EQ_WITH_EXCEPTION_ALL_ROUNDING(expected, actual, \
expected_except) \
do { \
@ -504,4 +665,138 @@ private:
(expected_except)); \
} while (0)
#define ASSERT_EQ_ROUNDING_MODE(expected, actual, rounding_mode) \
do { \
using namespace LIBC_NAMESPACE::fputil::testing; \
ForceRoundingMode __r((rounding_mode)); \
if (__r.success) { \
ASSERT_EQ((expected), (actual)); \
} \
} while (0)
#define ASSERT_EQ_ROUNDING_NEAREST(expected, actual) \
ASSERT_EQ_ROUNDING_MODE((expected), (actual), RoundingMode::Nearest)
#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
#define ASSERT_EQ_ROUNDING_UPWARD(expected, actual) \
do { \
} while (0)
#define ASSERT_EQ_ROUNDING_DOWNWARD(expected, actual) \
do { \
} while (0)
#define ASSERT_EQ_ROUNDING_TOWARD_ZERO(expected, actual) \
do { \
} while (0)
#else // !LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
#define ASSERT_EQ_ROUNDING_UPWARD(expected, actual) \
ASSERT_EQ_ROUNDING_MODE((expected), (actual), RoundingMode::Upward)
#define ASSERT_EQ_ROUNDING_DOWNWARD(expected, actual) \
ASSERT_EQ_ROUNDING_MODE((expected), (actual), RoundingMode::Downward)
#define ASSERT_EQ_ROUNDING_TOWARD_ZERO(expected, actual) \
ASSERT_EQ_ROUNDING_MODE((expected), (actual), RoundingMode::TowardZero)
#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
#define ASSERT_EQ_ALL_ROUNDING_1(expected, actual) \
do { \
ASSERT_EQ_ROUNDING_NEAREST((expected), (actual)); \
ASSERT_EQ_ROUNDING_UPWARD((expected), (actual)); \
ASSERT_EQ_ROUNDING_DOWNWARD((expected), (actual)); \
ASSERT_EQ_ROUNDING_TOWARD_ZERO((expected), (actual)); \
} while (0)
#define ASSERT_EQ_ALL_ROUNDING_4(expected_nearest, expected_upward, \
expected_downward, expected_toward_zero, \
actual) \
do { \
ASSERT_EQ_ROUNDING_NEAREST((expected_nearest), (actual)); \
ASSERT_EQ_ROUNDING_UPWARD((expected_upward), (actual)); \
ASSERT_EQ_ROUNDING_DOWNWARD((expected_downward), (actual)); \
ASSERT_EQ_ROUNDING_TOWARD_ZERO((expected_toward_zero), (actual)); \
} while (0)
#define ASSERT_EQ_ALL_ROUNDING_UNSUPPORTED(...) \
static_assert(false, "Unsupported number of arguments")
#define ASSERT_EQ_ALL_ROUNDING_GET_6TH_ARG(ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, \
...) \
ARG6
#define ASSERT_EQ_ALL_ROUNDING_SELECTION(...) \
ASSERT_EQ_ALL_ROUNDING_GET_6TH_ARG(__VA_ARGS__, ASSERT_EQ_ALL_ROUNDING_4, \
ASSERT_EQ_ALL_ROUNDING_UNSUPPORTED, \
ASSERT_EQ_ALL_ROUNDING_UNSUPPORTED, \
ASSERT_EQ_ALL_ROUNDING_1)
#define ASSERT_EQ_ALL_ROUNDING(...) \
ASSERT_EQ_ALL_ROUNDING_SELECTION(__VA_ARGS__)(__VA_ARGS__)
#define EXPECT_EQ_ROUNDING_MODE(expected, actual, rounding_mode) \
do { \
using namespace LIBC_NAMESPACE::fputil::testing; \
ForceRoundingMode __r((rounding_mode)); \
if (__r.success) { \
EXPECT_EQ((expected), (actual)); \
} \
} while (0)
#define EXPECT_EQ_ROUNDING_NEAREST(expected, actual) \
EXPECT_EQ_ROUNDING_MODE((expected), (actual), RoundingMode::Nearest)
#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
#define EXPECT_EQ_ROUNDING_UPWARD(expected, actual) \
do { \
} while (0)
#define EXPECT_EQ_ROUNDING_DOWNWARD(expected, actual) \
do { \
} while (0)
#define EXPECT_EQ_ROUNDING_TOWARD_ZERO(expected, actual) \
do { \
} while (0)
#else // !LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
#define EXPECT_EQ_ROUNDING_UPWARD(expected, actual) \
EXPECT_EQ_ROUNDING_MODE((expected), (actual), RoundingMode::Upward)
#define EXPECT_EQ_ROUNDING_DOWNWARD(expected, actual) \
EXPECT_EQ_ROUNDING_MODE((expected), (actual), RoundingMode::Downward)
#define EXPECT_EQ_ROUNDING_TOWARD_ZERO(expected, actual) \
EXPECT_EQ_ROUNDING_MODE((expected), (actual), RoundingMode::TowardZero)
#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
#define EXPECT_EQ_ALL_ROUNDING_1(expected, actual) \
do { \
EXPECT_EQ_ROUNDING_NEAREST((expected), (actual)); \
EXPECT_EQ_ROUNDING_UPWARD((expected), (actual)); \
EXPECT_EQ_ROUNDING_DOWNWARD((expected), (actual)); \
EXPECT_EQ_ROUNDING_TOWARD_ZERO((expected), (actual)); \
} while (0)
#define EXPECT_EQ_ALL_ROUNDING_4(expected_nearest, expected_upward, \
expected_downward, expected_toward_zero, \
actual) \
do { \
EXPECT_EQ_ROUNDING_NEAREST((expected_nearest), (actual)); \
EXPECT_EQ_ROUNDING_UPWARD((expected_upward), (actual)); \
EXPECT_EQ_ROUNDING_DOWNWARD((expected_downward), (actual)); \
EXPECT_EQ_ROUNDING_TOWARD_ZERO((expected_toward_zero), (actual)); \
} while (0)
#define EXPECT_EQ_ALL_ROUNDING_UNSUPPORTED(...) \
static_assert(false, "Unsupported number of arguments")
#define EXPECT_EQ_ALL_ROUNDING_GET_6TH_ARG(ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, \
...) \
ARG6
#define EXPECT_EQ_ALL_ROUNDING_SELECTION(...) \
EXPECT_EQ_ALL_ROUNDING_GET_6TH_ARG(__VA_ARGS__, EXPECT_EQ_ALL_ROUNDING_4, \
EXPECT_EQ_ALL_ROUNDING_UNSUPPORTED, \
EXPECT_EQ_ALL_ROUNDING_UNSUPPORTED, \
EXPECT_EQ_ALL_ROUNDING_1)
#define EXPECT_EQ_ALL_ROUNDING(...) \
EXPECT_EQ_ALL_ROUNDING_SELECTION(__VA_ARGS__)(__VA_ARGS__)
#endif // LLVM_LIBC_TEST_UNITTEST_FPMATCHER_H

View file

@ -34,6 +34,11 @@ int get_fe_rounding(RoundingMode mode) {
}
ForceRoundingMode::ForceRoundingMode(RoundingMode mode) {
#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
old_rounding_mode = FE_TONEAREST;
rounding_mode = FE_TONEAREST;
success = (mode == RoundingMode::Nearest);
#else
old_rounding_mode = quick_get_round();
rounding_mode = get_fe_rounding(mode);
if (old_rounding_mode != rounding_mode) {
@ -42,11 +47,14 @@ ForceRoundingMode::ForceRoundingMode(RoundingMode mode) {
} else {
success = true;
}
#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
}
ForceRoundingMode::~ForceRoundingMode() {
#ifndef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
if (old_rounding_mode != rounding_mode)
set_round(old_rounding_mode);
#endif
}
} // namespace testing

View file

@ -12,7 +12,6 @@
#undef LIBC_MATH_USE_SYSTEM_FENV
#include "src/__support/CPP/algorithm.h"
#include "src/__support/FPUtil/FEnvImpl.h"
#include "src/__support/FPUtil/FPBits.h"
#include "test/UnitTest/FEnvSafeTest.h"
#include "test/UnitTest/FPMatcher.h"
@ -25,9 +24,6 @@
namespace mpfr = LIBC_NAMESPACE::testing::mpfr;
using LIBC_NAMESPACE::Sign;
static constexpr int ROUNDING_MODES[4] = {FE_UPWARD, FE_DOWNWARD, FE_TOWARDZERO,
FE_TONEAREST};
template <typename T>
class RIntTestTemplate : public LIBC_NAMESPACE::testing::FEnvSafeTest {
public:
@ -35,6 +31,7 @@ public:
private:
using FPBits = LIBC_NAMESPACE::fputil::FPBits<T>;
using FPTest = LIBC_NAMESPACE::testing::FPTest<T>;
using StorageType = typename FPBits::StorageType;
const T inf = FPBits::inf(Sign::POS).get_val();
@ -50,56 +47,46 @@ private:
static constexpr StorageType MIN_NORMAL = FPBits::min_normal().uintval();
static constexpr StorageType MAX_NORMAL = FPBits::max_normal().uintval();
static inline mpfr::RoundingMode to_mpfr_rounding_mode(int mode) {
switch (mode) {
case FE_UPWARD:
return mpfr::RoundingMode::Upward;
case FE_DOWNWARD:
return mpfr::RoundingMode::Downward;
case FE_TOWARDZERO:
return mpfr::RoundingMode::TowardZero;
case FE_TONEAREST:
return mpfr::RoundingMode::Nearest;
default:
__builtin_unreachable();
}
}
public:
void testSpecialNumbers(RIntFunc func) {
for (int mode : ROUNDING_MODES) {
LIBC_NAMESPACE::fputil::set_round(mode);
ASSERT_FP_EQ(inf, func(inf));
ASSERT_FP_EQ(neg_inf, func(neg_inf));
ASSERT_FP_EQ(nan, func(nan));
ASSERT_FP_EQ(zero, func(zero));
ASSERT_FP_EQ(neg_zero, func(neg_zero));
}
ASSERT_FP_EQ_ALL_ROUNDING(inf, func(inf));
ASSERT_FP_EQ_ALL_ROUNDING(neg_inf, func(neg_inf));
ASSERT_FP_EQ_ALL_ROUNDING(nan, func(nan));
ASSERT_FP_EQ_ALL_ROUNDING(zero, func(zero));
ASSERT_FP_EQ_ALL_ROUNDING(neg_zero, func(neg_zero));
}
void testRoundNumbers(RIntFunc func) {
for (int mode : ROUNDING_MODES) {
LIBC_NAMESPACE::fputil::set_round(mode);
mpfr::RoundingMode mpfr_mode = to_mpfr_rounding_mode(mode);
ASSERT_FP_EQ(func(T(1.0)), mpfr::round(T(1.0), mpfr_mode));
ASSERT_FP_EQ(func(T(-1.0)), mpfr::round(T(-1.0), mpfr_mode));
ASSERT_FP_EQ(func(T(10.0)), mpfr::round(T(10.0), mpfr_mode));
ASSERT_FP_EQ(func(T(-10.0)), mpfr::round(T(-10.0), mpfr_mode));
ASSERT_FP_EQ(func(T(1234.0)), mpfr::round(T(1234.0), mpfr_mode));
ASSERT_FP_EQ(func(T(-1234.0)), mpfr::round(T(-1234.0), mpfr_mode));
for (auto mpfr_mode : FPTest::ROUNDING_MODES) {
ASSERT_FP_EQ_ROUNDING_MODE(mpfr::round(T(1.0), mpfr_mode), func(T(1.0)),
mpfr_mode);
ASSERT_FP_EQ_ROUNDING_MODE(mpfr::round(T(-1.0), mpfr_mode), func(T(-1.0)),
mpfr_mode);
ASSERT_FP_EQ_ROUNDING_MODE(mpfr::round(T(10.0), mpfr_mode), func(T(10.0)),
mpfr_mode);
ASSERT_FP_EQ_ROUNDING_MODE(mpfr::round(T(-10.0), mpfr_mode),
func(T(-10.0)), mpfr_mode);
ASSERT_FP_EQ_ROUNDING_MODE(mpfr::round(T(1234.0), mpfr_mode),
func(T(1234.0)), mpfr_mode);
ASSERT_FP_EQ_ROUNDING_MODE(mpfr::round(T(-1234.0), mpfr_mode),
func(T(-1234.0)), mpfr_mode);
}
}
void testFractions(RIntFunc func) {
for (int mode : ROUNDING_MODES) {
LIBC_NAMESPACE::fputil::set_round(mode);
mpfr::RoundingMode mpfr_mode = to_mpfr_rounding_mode(mode);
ASSERT_FP_EQ(func(T(0.5)), mpfr::round(T(0.5), mpfr_mode));
ASSERT_FP_EQ(func(T(-0.5)), mpfr::round(T(-0.5), mpfr_mode));
ASSERT_FP_EQ(func(T(0.115)), mpfr::round(T(0.115), mpfr_mode));
ASSERT_FP_EQ(func(T(-0.115)), mpfr::round(T(-0.115), mpfr_mode));
ASSERT_FP_EQ(func(T(0.715)), mpfr::round(T(0.715), mpfr_mode));
ASSERT_FP_EQ(func(T(-0.715)), mpfr::round(T(-0.715), mpfr_mode));
for (auto mpfr_mode : FPTest::ROUNDING_MODES) {
ASSERT_FP_EQ_ROUNDING_MODE(mpfr::round(T(0.5), mpfr_mode), func(T(0.5)),
mpfr_mode);
ASSERT_FP_EQ_ROUNDING_MODE(mpfr::round(T(-0.5), mpfr_mode), func(T(-0.5)),
mpfr_mode);
ASSERT_FP_EQ_ROUNDING_MODE(mpfr::round(T(0.115), mpfr_mode),
func(T(0.115)), mpfr_mode);
ASSERT_FP_EQ_ROUNDING_MODE(mpfr::round(T(-0.115), mpfr_mode),
func(T(-0.115)), mpfr_mode);
ASSERT_FP_EQ_ROUNDING_MODE(mpfr::round(T(0.715), mpfr_mode),
func(T(0.715)), mpfr_mode);
ASSERT_FP_EQ_ROUNDING_MODE(mpfr::round(T(-0.715), mpfr_mode),
func(T(-0.715)), mpfr_mode);
}
}
@ -110,10 +97,9 @@ public:
StorageType(1));
for (StorageType i = MIN_SUBNORMAL; i <= MAX_SUBNORMAL; i += STEP) {
T x = FPBits(i).get_val();
for (int mode : ROUNDING_MODES) {
LIBC_NAMESPACE::fputil::set_round(mode);
mpfr::RoundingMode mpfr_mode = to_mpfr_rounding_mode(mode);
ASSERT_FP_EQ(func(x), mpfr::round(x, mpfr_mode));
for (auto mpfr_mode : FPTest::ROUNDING_MODES) {
ASSERT_FP_EQ_ROUNDING_MODE(mpfr::round(x, mpfr_mode), func(x),
mpfr_mode);
}
}
}
@ -131,10 +117,9 @@ public:
if (xbits.is_nan())
continue;
for (int mode : ROUNDING_MODES) {
LIBC_NAMESPACE::fputil::set_round(mode);
mpfr::RoundingMode mpfr_mode = to_mpfr_rounding_mode(mode);
ASSERT_FP_EQ(func(x), mpfr::round(x, mpfr_mode));
for (auto mpfr_mode : FPTest::ROUNDING_MODES) {
ASSERT_FP_EQ_ROUNDING_MODE(mpfr::round(x, mpfr_mode), func(x),
mpfr_mode);
}
}
}

View file

@ -9,6 +9,8 @@
#ifndef LLVM_LIBC_TEST_SRC_MATH_ROUNDTOINTEGERTEST_H
#define LLVM_LIBC_TEST_SRC_MATH_ROUNDTOINTEGERTEST_H
#include "test/UnitTest/RoundingModeUtils.h"
#include <cfenv>
#undef LIBC_MATH_USE_SYSTEM_FENV
#include "src/__support/CPP/algorithm.h"
@ -25,9 +27,6 @@
namespace mpfr = LIBC_NAMESPACE::testing::mpfr;
using LIBC_NAMESPACE::Sign;
static constexpr int ROUNDING_MODES[4] = {FE_UPWARD, FE_DOWNWARD, FE_TOWARDZERO,
FE_TONEAREST};
template <typename FloatType, typename IntType, bool TestModes = false>
class RoundToIntegerTestTemplate
: public LIBC_NAMESPACE::testing::FEnvSafeTest {
@ -36,6 +35,8 @@ public:
private:
using FPBits = LIBC_NAMESPACE::fputil::FPBits<FloatType>;
using FPTest = LIBC_NAMESPACE::testing::FPTest<FloatType>;
using RoundingMode = LIBC_NAMESPACE::fputil::testing::RoundingMode;
using StorageType = typename FPBits::StorageType;
const FloatType zero = FPBits::zero().get_val();
@ -59,9 +60,7 @@ private:
IntType expected, bool expectError) {
libc_errno = 0;
LIBC_NAMESPACE::fputil::clear_except(FE_ALL_EXCEPT);
ASSERT_EQ(func(input), expected);
// TODO: Handle the !expectError case. It used to expect
// 0 for errno and exceptions, but this doesn't hold for
// all math functions using RoundToInteger test:
@ -72,21 +71,6 @@ private:
}
}
static inline mpfr::RoundingMode to_mpfr_rounding_mode(int mode) {
switch (mode) {
case FE_UPWARD:
return mpfr::RoundingMode::Upward;
case FE_DOWNWARD:
return mpfr::RoundingMode::Downward;
case FE_TOWARDZERO:
return mpfr::RoundingMode::TowardZero;
case FE_TONEAREST:
return mpfr::RoundingMode::Nearest;
default:
__builtin_unreachable();
}
}
public:
void SetUp() override {
LIBC_NAMESPACE::testing::FEnvSafeTest::SetUp();
@ -99,9 +83,13 @@ public:
}
}
void do_infinity_and_na_n_test(RoundToIntegerFunc func) {
test_one_input(func, inf, INTEGER_MAX, true);
test_one_input(func, neg_inf, INTEGER_MIN, true);
void testInfinityAndNaN(RoundToIntegerFunc func) {
libc_errno = 0;
LIBC_NAMESPACE::fputil::clear_except(FE_ALL_EXCEPT);
ASSERT_EQ_ALL_ROUNDING(INTEGER_MAX, func(inf));
ASSERT_EQ_ALL_ROUNDING(INTEGER_MIN, func(neg_inf));
ASSERT_FP_EXCEPTION(FE_INVALID);
ASSERT_MATH_ERRNO(EDOM);
// This is currently never enabled, the
// LLVM_LIBC_IMPLEMENTATION_DEFINED_TEST_BEHAVIOR CMake option in
// libc/CMakeLists.txt is not forwarded to C++.
@ -111,26 +99,15 @@ public:
#endif // LIBC_COPT_IMPLEMENTATION_DEFINED_TEST_BEHAVIOR
}
void testInfinityAndNaN(RoundToIntegerFunc func) {
if (TestModes) {
for (int mode : ROUNDING_MODES) {
LIBC_NAMESPACE::fputil::set_round(mode);
do_infinity_and_na_n_test(func);
}
} else {
do_infinity_and_na_n_test(func);
}
}
void do_round_numbers_test(RoundToIntegerFunc func) {
test_one_input(func, zero, IntType(0), false);
test_one_input(func, neg_zero, IntType(0), false);
test_one_input(func, FloatType(1.0), IntType(1), false);
test_one_input(func, FloatType(-1.0), IntType(-1), false);
test_one_input(func, FloatType(10.0), IntType(10), false);
test_one_input(func, FloatType(-10.0), IntType(-10), false);
test_one_input(func, FloatType(1232.0), IntType(1232), false);
test_one_input(func, FloatType(-1232.0), IntType(-1232), false);
void testRoundNumbers(RoundToIntegerFunc func) {
ASSERT_EQ_ALL_ROUNDING(IntType(0), func(zero));
ASSERT_EQ_ALL_ROUNDING(IntType(0), func(neg_zero));
ASSERT_EQ_ALL_ROUNDING(IntType(1), func(FloatType(1.0)));
ASSERT_EQ_ALL_ROUNDING(IntType(-1), func(FloatType(-1.0)));
ASSERT_EQ_ALL_ROUNDING(IntType(10), func(FloatType(10.0)));
ASSERT_EQ_ALL_ROUNDING(IntType(-10), func(FloatType(-10.0)));
ASSERT_EQ_ALL_ROUNDING(IntType(1232), func(FloatType(1232.0)));
ASSERT_EQ_ALL_ROUNDING(IntType(-1232), func(FloatType(-1232.0)));
// The rest of this function compares with an equivalent MPFR function
// which rounds floating point numbers to long values. There is no MPFR
@ -155,49 +132,31 @@ public:
long mpfr_result;
bool erangeflag = mpfr::round_to_long(x, mpfr_result);
ASSERT_FALSE(erangeflag);
test_one_input(func, x, mpfr_result, false);
ASSERT_EQ_ALL_ROUNDING(IntType(mpfr_result), func(x));
}
void testRoundNumbers(RoundToIntegerFunc func) {
if (TestModes) {
for (int mode : ROUNDING_MODES) {
LIBC_NAMESPACE::fputil::set_round(mode);
do_round_numbers_test(func);
}
} else {
do_round_numbers_test(func);
}
}
void do_fractions_test(RoundToIntegerFunc func, int mode) {
void testFractions(RoundToIntegerFunc func) {
constexpr FloatType FRACTIONS[] = {
FloatType(0.5), FloatType(-0.5), FloatType(0.115),
FloatType(-0.115), FloatType(0.715), FloatType(-0.715),
};
for (FloatType x : FRACTIONS) {
long mpfr_long_result;
bool erangeflag;
if (TestModes)
erangeflag = mpfr::round_to_long(x, to_mpfr_rounding_mode(mode),
mpfr_long_result);
else
erangeflag = mpfr::round_to_long(x, mpfr_long_result);
ASSERT_FALSE(erangeflag);
IntType mpfr_result = mpfr_long_result;
test_one_input(func, x, mpfr_result, false);
}
}
void testFractions(RoundToIntegerFunc func) {
if (TestModes) {
for (int mode : ROUNDING_MODES) {
LIBC_NAMESPACE::fputil::set_round(mode);
do_fractions_test(func, mode);
for (auto mpfr_mode : FPTest::ROUNDING_MODES) {
for (FloatType x : FRACTIONS) {
long mpfr_long_result;
bool erangeflag = mpfr::round_to_long(x, mpfr_mode, mpfr_long_result);
ASSERT_FALSE(erangeflag);
ASSERT_EQ_ROUNDING_MODE(IntType(mpfr_long_result), func(x),
mpfr_mode);
}
}
} else {
// Passing 0 for mode has no effect as it is not used in doFractionsTest
// when `TestModes` is false;
do_fractions_test(func, 0);
for (FloatType x : FRACTIONS) {
long mpfr_long_result;
bool erangeflag = mpfr::round_to_long(x, mpfr_long_result);
ASSERT_FALSE(erangeflag);
test_one_input(func, x, IntType(mpfr_long_result), false);
}
}
}
@ -223,18 +182,12 @@ public:
FloatType x = bits.get_val();
if (TestModes) {
for (int m : ROUNDING_MODES) {
LIBC_NAMESPACE::fputil::set_round(m);
long mpfr_long_result;
bool erangeflag =
mpfr::round_to_long(x, to_mpfr_rounding_mode(m), mpfr_long_result);
ASSERT_TRUE(erangeflag);
test_one_input(func, x, INTEGER_MIN, true);
for (auto m : FPTest::ROUNDING_MODES) {
LIBC_NAMESPACE::fputil::testing::ForceRoundingMode _r(m);
if (_r.success)
test_one_input(func, x, INTEGER_MIN, true);
}
} else {
long mpfr_long_result;
bool erangeflag = mpfr::round_to_long(x, mpfr_long_result);
ASSERT_TRUE(erangeflag);
test_one_input(func, x, INTEGER_MIN, true);
}
}
@ -251,26 +204,18 @@ public:
// All subnormal numbers should round to zero.
if (TestModes) {
if (x > 0) {
LIBC_NAMESPACE::fputil::set_round(FE_UPWARD);
test_one_input(func, x, IntType(1), false);
LIBC_NAMESPACE::fputil::set_round(FE_DOWNWARD);
test_one_input(func, x, IntType(0), false);
LIBC_NAMESPACE::fputil::set_round(FE_TOWARDZERO);
test_one_input(func, x, IntType(0), false);
LIBC_NAMESPACE::fputil::set_round(FE_TONEAREST);
test_one_input(func, x, IntType(0), false);
ASSERT_EQ_ROUNDING_UPWARD(IntType(1), func(x));
ASSERT_EQ_ROUNDING_DOWNWARD(IntType(0), func(x));
ASSERT_EQ_ROUNDING_TOWARD_ZERO(IntType(0), func(x));
ASSERT_EQ_ROUNDING_NEAREST(IntType(0), func(x));
} else {
LIBC_NAMESPACE::fputil::set_round(FE_UPWARD);
test_one_input(func, x, IntType(0), false);
LIBC_NAMESPACE::fputil::set_round(FE_DOWNWARD);
test_one_input(func, x, IntType(-1), false);
LIBC_NAMESPACE::fputil::set_round(FE_TOWARDZERO);
test_one_input(func, x, IntType(0), false);
LIBC_NAMESPACE::fputil::set_round(FE_TONEAREST);
test_one_input(func, x, IntType(0), false);
ASSERT_EQ_ROUNDING_UPWARD(IntType(0), func(x));
ASSERT_EQ_ROUNDING_DOWNWARD(IntType(-1), func(x));
ASSERT_EQ_ROUNDING_TOWARD_ZERO(IntType(0), func(x));
ASSERT_EQ_ROUNDING_NEAREST(IntType(0), func(x));
}
} else {
test_one_input(func, x, 0L, false);
test_one_input(func, x, IntType(0), false);
}
}
}
@ -297,25 +242,24 @@ public:
continue;
if (TestModes) {
for (int m : ROUNDING_MODES) {
for (auto m : FPTest::ROUNDING_MODES) {
long mpfr_long_result;
bool erangeflag = mpfr::round_to_long(x, to_mpfr_rounding_mode(m),
mpfr_long_result);
IntType mpfr_result = mpfr_long_result;
LIBC_NAMESPACE::fputil::set_round(m);
if (erangeflag)
test_one_input(func, x, x > 0 ? INTEGER_MAX : INTEGER_MIN, true);
else
test_one_input(func, x, mpfr_result, false);
bool erangeflag = mpfr::round_to_long(x, m, mpfr_long_result);
LIBC_NAMESPACE::fputil::testing::ForceRoundingMode _r(m);
if (_r.success) {
if (erangeflag)
test_one_input(func, x, x > 0 ? INTEGER_MAX : INTEGER_MIN, true);
else
test_one_input(func, x, IntType(mpfr_long_result), false);
}
}
} else {
long mpfr_long_result;
bool erangeflag = mpfr::round_to_long(x, mpfr_long_result);
IntType mpfr_result = mpfr_long_result;
if (erangeflag)
test_one_input(func, x, x > 0 ? INTEGER_MAX : INTEGER_MIN, true);
else
test_one_input(func, x, mpfr_result, false);
test_one_input(func, x, IntType(mpfr_long_result), false);
}
}
}

View file

@ -19,9 +19,6 @@
using LIBC_NAMESPACE::Sign;
static constexpr int ROUNDING_MODES[4] = {FE_UPWARD, FE_DOWNWARD, FE_TOWARDZERO,
FE_TONEAREST};
template <typename T>
class NearbyIntTestTemplate : public LIBC_NAMESPACE::testing::Test {

View file

@ -22,9 +22,6 @@
using LIBC_NAMESPACE::Sign;
static constexpr int ROUNDING_MODES[4] = {FE_UPWARD, FE_DOWNWARD, FE_TOWARDZERO,
FE_TONEAREST};
template <typename T>
class RIntTestTemplate : public LIBC_NAMESPACE::testing::FEnvSafeTest {
public:
@ -42,14 +39,11 @@ private:
public:
void testSpecialNumbers(RIntFunc func) {
for (int mode : ROUNDING_MODES) {
LIBC_NAMESPACE::fputil::set_round(mode);
ASSERT_FP_EQ(inf, func(inf));
ASSERT_FP_EQ(neg_inf, func(neg_inf));
ASSERT_FP_EQ(nan, func(nan));
ASSERT_FP_EQ(zero, func(zero));
ASSERT_FP_EQ(neg_zero, func(neg_zero));
}
ASSERT_FP_EQ_ALL_ROUNDING(inf, func(inf));
ASSERT_FP_EQ_ALL_ROUNDING(neg_inf, func(neg_inf));
ASSERT_FP_EQ_ALL_ROUNDING(nan, func(nan));
ASSERT_FP_EQ_ALL_ROUNDING(zero, func(zero));
ASSERT_FP_EQ_ALL_ROUNDING(neg_zero, func(neg_zero));
}
};

View file

@ -9,6 +9,7 @@
#ifndef LLVM_LIBC_TEST_SRC_MATH_SMOKE_ROUNDTOINTEGERTEST_H
#define LLVM_LIBC_TEST_SRC_MATH_SMOKE_ROUNDTOINTEGERTEST_H
#include "test/UnitTest/RoundingModeUtils.h"
#undef LIBC_MATH_USE_SYSTEM_FENV
#include "src/__support/CPP/algorithm.h"
@ -21,9 +22,6 @@
#include "hdr/math_macros.h"
static constexpr int ROUNDING_MODES[4] = {FE_UPWARD, FE_DOWNWARD, FE_TOWARDZERO,
FE_TONEAREST};
template <typename FloatType, typename IntType, bool TestModes = false>
class RoundToIntegerTestTemplate
: public LIBC_NAMESPACE::testing::FEnvSafeTest {
@ -32,6 +30,8 @@ public:
private:
DECLARE_SPECIAL_CONSTANTS(FloatType)
using FPTest = LIBC_NAMESPACE::testing::FPTest<FloatType>;
using RoundingMode = LIBC_NAMESPACE::fputil::testing::RoundingMode;
static constexpr StorageType MAX_SUBNORMAL =
FPBits::max_subnormal().uintval();
@ -71,9 +71,13 @@ public:
}
}
void do_infinity_and_na_n_test(RoundToIntegerFunc func) {
test_one_input(func, inf, INTEGER_MAX, true);
test_one_input(func, neg_inf, INTEGER_MIN, true);
void testInfinityAndNaN(RoundToIntegerFunc func) {
libc_errno = 0;
LIBC_NAMESPACE::fputil::clear_except(FE_ALL_EXCEPT);
ASSERT_EQ_ALL_ROUNDING(INTEGER_MAX, func(inf));
ASSERT_EQ_ALL_ROUNDING(INTEGER_MIN, func(neg_inf));
ASSERT_FP_EXCEPTION(FE_INVALID);
ASSERT_MATH_ERRNO(EDOM);
// This is currently never enabled, the
// LLVM_LIBC_IMPLEMENTATION_DEFINED_TEST_BEHAVIOR CMake option in
// libc/CMakeLists.txt is not forwarded to C++.
@ -83,37 +87,15 @@ public:
#endif // LIBC_COPT_IMPLEMENTATION_DEFINED_TEST_BEHAVIOR
}
void testInfinityAndNaN(RoundToIntegerFunc func) {
if (TestModes) {
for (int mode : ROUNDING_MODES) {
LIBC_NAMESPACE::fputil::set_round(mode);
do_infinity_and_na_n_test(func);
}
} else {
do_infinity_and_na_n_test(func);
}
}
void do_round_numbers_test(RoundToIntegerFunc func) {
test_one_input(func, zero, IntType(0), false);
test_one_input(func, neg_zero, IntType(0), false);
test_one_input(func, FloatType(1.0), IntType(1), false);
test_one_input(func, FloatType(-1.0), IntType(-1), false);
test_one_input(func, FloatType(10.0), IntType(10), false);
test_one_input(func, FloatType(-10.0), IntType(-10), false);
test_one_input(func, FloatType(1232.0), IntType(1232), false);
test_one_input(func, FloatType(-1232.0), IntType(-1232), false);
}
void testRoundNumbers(RoundToIntegerFunc func) {
if (TestModes) {
for (int mode : ROUNDING_MODES) {
LIBC_NAMESPACE::fputil::set_round(mode);
do_round_numbers_test(func);
}
} else {
do_round_numbers_test(func);
}
ASSERT_EQ_ALL_ROUNDING(IntType(0), func(zero));
ASSERT_EQ_ALL_ROUNDING(IntType(0), func(neg_zero));
ASSERT_EQ_ALL_ROUNDING(IntType(1), func(FloatType(1.0)));
ASSERT_EQ_ALL_ROUNDING(IntType(-1), func(FloatType(-1.0)));
ASSERT_EQ_ALL_ROUNDING(IntType(10), func(FloatType(10.0)));
ASSERT_EQ_ALL_ROUNDING(IntType(-10), func(FloatType(-10.0)));
ASSERT_EQ_ALL_ROUNDING(IntType(1232), func(FloatType(1232.0)));
ASSERT_EQ_ALL_ROUNDING(IntType(-1232), func(FloatType(-1232.0)));
}
void testSubnormalRange(RoundToIntegerFunc func) {
@ -128,27 +110,19 @@ public:
continue;
// All subnormal numbers should round to zero.
if (TestModes) {
if (x > zero) {
LIBC_NAMESPACE::fputil::set_round(FE_UPWARD);
test_one_input(func, x, IntType(1), false);
LIBC_NAMESPACE::fputil::set_round(FE_DOWNWARD);
test_one_input(func, x, IntType(0), false);
LIBC_NAMESPACE::fputil::set_round(FE_TOWARDZERO);
test_one_input(func, x, IntType(0), false);
LIBC_NAMESPACE::fputil::set_round(FE_TONEAREST);
test_one_input(func, x, IntType(0), false);
if (x > 0) {
ASSERT_EQ_ROUNDING_UPWARD(IntType(1), func(x));
ASSERT_EQ_ROUNDING_DOWNWARD(IntType(0), func(x));
ASSERT_EQ_ROUNDING_TOWARD_ZERO(IntType(0), func(x));
ASSERT_EQ_ROUNDING_NEAREST(IntType(0), func(x));
} else {
LIBC_NAMESPACE::fputil::set_round(FE_UPWARD);
test_one_input(func, x, IntType(0), false);
LIBC_NAMESPACE::fputil::set_round(FE_DOWNWARD);
test_one_input(func, x, IntType(-1), false);
LIBC_NAMESPACE::fputil::set_round(FE_TOWARDZERO);
test_one_input(func, x, IntType(0), false);
LIBC_NAMESPACE::fputil::set_round(FE_TONEAREST);
test_one_input(func, x, IntType(0), false);
ASSERT_EQ_ROUNDING_UPWARD(IntType(0), func(x));
ASSERT_EQ_ROUNDING_DOWNWARD(IntType(-1), func(x));
ASSERT_EQ_ROUNDING_TOWARD_ZERO(IntType(0), func(x));
ASSERT_EQ_ROUNDING_NEAREST(IntType(0), func(x));
}
} else {
test_one_input(func, x, 0L, false);
test_one_input(func, x, IntType(0), false);
}
}
}

View file

@ -396,28 +396,78 @@ template <typename T> bool round_to_long(T x, RoundingMode mode, long &result);
input, match_value, ulp_tolerance, \
LIBC_NAMESPACE::testing::mpfr::RoundingMode::Nearest))
#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
#define EXPECT_MPFR_MATCH_ROUNDING(op, input, match_value, ulp_tolerance, \
rounding) \
EXPECT_MPFR_MATCH_DEFAULT(op, input, match_value, ulp_tolerance)
#else // !LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
#define EXPECT_MPFR_MATCH_ROUNDING(op, input, match_value, ulp_tolerance, \
rounding) \
EXPECT_THAT(match_value, \
LIBC_NAMESPACE::testing::mpfr::get_mpfr_matcher<op>( \
input, match_value, ulp_tolerance, rounding))
#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
#define EXPECT_MPFR_MATCH(...) \
GET_MPFR_MACRO(__VA_ARGS__, EXPECT_MPFR_MATCH_ROUNDING, \
EXPECT_MPFR_MATCH_DEFAULT, GET_MPFR_DUMMY_ARG) \
(__VA_ARGS__)
#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
#define TEST_MPFR_MATCH_ROUNDING(op, input, match_value, ulp_tolerance, \
rounding) \
LIBC_NAMESPACE::testing::mpfr::get_mpfr_matcher<op>( \
input, match_value, ulp_tolerance, \
LIBC_NAMESPACE::testing::mpfr::RoundingMode::Nearest) \
.match(match_value)
#else // !LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
#define TEST_MPFR_MATCH_ROUNDING(op, input, match_value, ulp_tolerance, \
rounding) \
LIBC_NAMESPACE::testing::mpfr::get_mpfr_matcher<op>(input, match_value, \
ulp_tolerance, rounding) \
.match(match_value)
#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
#define TEST_MPFR_MATCH(...) \
GET_MPFR_MACRO(__VA_ARGS__, TEST_MPFR_MATCH_ROUNDING, \
EXPECT_MPFR_MATCH_DEFAULT, GET_MPFR_DUMMY_ARG) \
(__VA_ARGS__)
#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
#define TEST_MPFR_MATCH_ROUNDING_SILENTLY(op, input, match_value, \
ulp_tolerance, rounding) \
LIBC_NAMESPACE::testing::mpfr::get_silent_mpfr_matcher<op>( \
input, match_value, ulp_tolerance, \
LIBC_NAMESPACE::testing::mpfr::RoundingMode::Nearest) \
.match(match_value)
#define EXPECT_MPFR_MATCH_ALL_ROUNDING(op, input, match_value, ulp_tolerance) \
{ \
namespace mpfr = LIBC_NAMESPACE::testing::mpfr; \
mpfr::ForceRoundingMode __r1(mpfr::RoundingMode::Nearest); \
if (__r1.success) { \
EXPECT_MPFR_MATCH(op, input, match_value, ulp_tolerance, \
mpfr::RoundingMode::Nearest); \
} \
}
#else // !LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
#define TEST_MPFR_MATCH_ROUNDING_SILENTLY(op, input, match_value, \
ulp_tolerance, rounding) \
LIBC_NAMESPACE::testing::mpfr::get_silent_mpfr_matcher<op>( \
input, match_value, ulp_tolerance, rounding) \
.match(match_value)
#define EXPECT_MPFR_MATCH_ALL_ROUNDING(op, input, match_value, ulp_tolerance) \
{ \
namespace mpfr = LIBC_NAMESPACE::testing::mpfr; \
@ -443,29 +493,48 @@ template <typename T> bool round_to_long(T x, RoundingMode mode, long &result);
} \
}
#define TEST_MPFR_MATCH_ROUNDING_SILENTLY(op, input, match_value, \
ulp_tolerance, rounding) \
LIBC_NAMESPACE::testing::mpfr::get_silent_mpfr_matcher<op>( \
input, match_value, ulp_tolerance, rounding) \
.match(match_value)
#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
#define ASSERT_MPFR_MATCH_DEFAULT(op, input, match_value, ulp_tolerance) \
ASSERT_THAT(match_value, \
LIBC_NAMESPACE::testing::mpfr::get_mpfr_matcher<op>( \
input, match_value, ulp_tolerance, \
LIBC_NAMESPACE::testing::mpfr::RoundingMode::Nearest))
#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
#define ASSERT_MPFR_MATCH_ROUNDING(op, input, match_value, ulp_tolerance, \
rounding) \
ASSERT_MPFR_MATCH_DEFAULT(op, input, match_value, ulp_tolerance)
#else // !LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
#define ASSERT_MPFR_MATCH_ROUNDING(op, input, match_value, ulp_tolerance, \
rounding) \
ASSERT_THAT(match_value, \
LIBC_NAMESPACE::testing::mpfr::get_mpfr_matcher<op>( \
input, match_value, ulp_tolerance, rounding))
#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
#define ASSERT_MPFR_MATCH(...) \
GET_MPFR_MACRO(__VA_ARGS__, ASSERT_MPFR_MATCH_ROUNDING, \
ASSERT_MPFR_MATCH_DEFAULT, GET_MPFR_DUMMY_ARG) \
(__VA_ARGS__)
#define ASSERT_MPFR_MATCH_DEFAULT(op, input, match_value, ulp_tolerance) \
ASSERT_THAT( \
match_value, \
LIBC_NAMESPACE::testing::mpfr::get_mpfr_matcher<op>( \
input, match_value, ulp_tolerance, mpfr::RoundingMode::Nearest))
#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
#define ASSERT_MPFR_MATCH_ALL_ROUNDING(op, input, match_value, ulp_tolerance) \
{ \
namespace mpfr = LIBC_NAMESPACE::testing::mpfr; \
mpfr::ForceRoundingMode __r1(mpfr::RoundingMode::Nearest); \
if (__r1.success) { \
ASSERT_MPFR_MATCH(op, input, match_value, ulp_tolerance, \
mpfr::RoundingMode::Nearest); \
} \
}
#else // !LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
#define ASSERT_MPFR_MATCH_ALL_ROUNDING(op, input, match_value, ulp_tolerance) \
{ \
namespace mpfr = LIBC_NAMESPACE::testing::mpfr; \
@ -491,4 +560,6 @@ template <typename T> bool round_to_long(T x, RoundingMode mode, long &result);
} \
}
#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
#endif // LLVM_LIBC_UTILS_MPFRWRAPPER_MPFRUTILS_H