[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:
parent
3b67307500
commit
582643f1ec
55 changed files with 885 additions and 277 deletions
|
|
@ -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": "",
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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).
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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).
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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).
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue