brintos

brintos / llvm-project-archived public Read only

0
0
Text · 13.6 KiB · e5e9386 Raw
348 lines · c
1//===-- Utility class to test different flavors of [l|ll]round --*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#ifndef LLVM_LIBC_TEST_SRC_MATH_ROUNDTOINTEGERTEST_H10#define LLVM_LIBC_TEST_SRC_MATH_ROUNDTOINTEGERTEST_H11 12#include "src/__support/CPP/algorithm.h"13#include "src/__support/FPUtil/FEnvImpl.h"14#include "src/__support/FPUtil/FPBits.h"15#include "src/__support/macros/properties/architectures.h"16#include "test/UnitTest/FEnvSafeTest.h"17#include "test/UnitTest/FPMatcher.h"18#include "test/UnitTest/Test.h"19#include "utils/MPFRWrapper/MPFRUtils.h"20 21#include "hdr/math_macros.h"22 23namespace mpfr = LIBC_NAMESPACE::testing::mpfr;24using LIBC_NAMESPACE::Sign;25 26static constexpr int ROUNDING_MODES[4] = {FE_UPWARD, FE_DOWNWARD, FE_TOWARDZERO,27                                          FE_TONEAREST};28 29template <typename FloatType, typename IntType, bool TestModes = false>30class RoundToIntegerTestTemplate31    : public LIBC_NAMESPACE::testing::FEnvSafeTest {32public:33  typedef IntType (*RoundToIntegerFunc)(FloatType);34 35private:36  using FPBits = LIBC_NAMESPACE::fputil::FPBits<FloatType>;37  using StorageType = typename FPBits::StorageType;38 39  const FloatType zero = FPBits::zero().get_val();40  const FloatType neg_zero = FPBits::zero(Sign::NEG).get_val();41  const FloatType inf = FPBits::inf().get_val();42  const FloatType neg_inf = FPBits::inf(Sign::NEG).get_val();43  const FloatType nan = FPBits::quiet_nan().get_val();44 45  static constexpr StorageType MAX_NORMAL = FPBits::max_normal().uintval();46  static constexpr StorageType MIN_NORMAL = FPBits::min_normal().uintval();47  static constexpr StorageType MAX_SUBNORMAL =48      FPBits::max_subnormal().uintval();49  static constexpr StorageType MIN_SUBNORMAL =50      FPBits::min_subnormal().uintval();51 52  static constexpr IntType INTEGER_MIN = IntType(1)53                                         << (sizeof(IntType) * 8 - 1);54  static constexpr IntType INTEGER_MAX = -(INTEGER_MIN + 1);55 56  void test_one_input(RoundToIntegerFunc func, FloatType input,57                      IntType expected, bool expectError) {58    libc_errno = 0;59    LIBC_NAMESPACE::fputil::clear_except(FE_ALL_EXCEPT);60 61    ASSERT_EQ(func(input), expected);62 63    // TODO: Handle the !expectError case. It used to expect64    // 0 for errno and exceptions, but this doesn't hold for65    // all math functions using RoundToInteger test:66    // https://github.com/llvm/llvm-project/pull/8881667    if (expectError) {68      ASSERT_FP_EXCEPTION(FE_INVALID);69      ASSERT_MATH_ERRNO(EDOM);70    }71  }72 73  static inline mpfr::RoundingMode to_mpfr_rounding_mode(int mode) {74    switch (mode) {75    case FE_UPWARD:76      return mpfr::RoundingMode::Upward;77    case FE_DOWNWARD:78      return mpfr::RoundingMode::Downward;79    case FE_TOWARDZERO:80      return mpfr::RoundingMode::TowardZero;81    case FE_TONEAREST:82      return mpfr::RoundingMode::Nearest;83    default:84      __builtin_unreachable();85    }86  }87 88public:89  void SetUp() override {90    LIBC_NAMESPACE::testing::FEnvSafeTest::SetUp();91 92    if (math_errhandling & MATH_ERREXCEPT) {93      // We will disable all exceptions so that the test will not94      // crash with SIGFPE. We can still use fetestexcept to check95      // if the appropriate flag was raised.96      LIBC_NAMESPACE::fputil::disable_except(FE_ALL_EXCEPT);97    }98  }99 100  void do_infinity_and_na_n_test(RoundToIntegerFunc func) {101    test_one_input(func, inf, INTEGER_MAX, true);102    test_one_input(func, neg_inf, INTEGER_MIN, true);103    // This is currently never enabled, the104    // LLVM_LIBC_IMPLEMENTATION_DEFINED_TEST_BEHAVIOR CMake option in105    // libc/CMakeLists.txt is not forwarded to C++.106#if LIBC_COPT_IMPLEMENTATION_DEFINED_TEST_BEHAVIOR107    // Result is not well-defined, we always returns INTEGER_MAX108    test_one_input(func, nan, INTEGER_MAX, true);109#endif // LIBC_COPT_IMPLEMENTATION_DEFINED_TEST_BEHAVIOR110  }111 112  void testInfinityAndNaN(RoundToIntegerFunc func) {113    if (TestModes) {114      for (int mode : ROUNDING_MODES) {115        LIBC_NAMESPACE::fputil::set_round(mode);116        do_infinity_and_na_n_test(func);117      }118    } else {119      do_infinity_and_na_n_test(func);120    }121  }122 123  void do_round_numbers_test(RoundToIntegerFunc func) {124    test_one_input(func, zero, IntType(0), false);125    test_one_input(func, neg_zero, IntType(0), false);126    test_one_input(func, FloatType(1.0), IntType(1), false);127    test_one_input(func, FloatType(-1.0), IntType(-1), false);128    test_one_input(func, FloatType(10.0), IntType(10), false);129    test_one_input(func, FloatType(-10.0), IntType(-10), false);130    test_one_input(func, FloatType(1232.0), IntType(1232), false);131    test_one_input(func, FloatType(-1232.0), IntType(-1232), false);132 133    // The rest of this function compares with an equivalent MPFR function134    // which rounds floating point numbers to long values. There is no MPFR135    // function to round to long long or wider integer values. So, we will136    // the remaining tests only if the width of IntType less than equal to that137    // of long.138    if (sizeof(IntType) > sizeof(long))139      return;140 141    constexpr int EXPONENT_LIMIT = sizeof(IntType) * 8 - 1;142    constexpr int BIASED_EXPONENT_LIMIT = EXPONENT_LIMIT + FPBits::EXP_BIAS;143    if (BIASED_EXPONENT_LIMIT > FPBits::MAX_BIASED_EXPONENT)144      return;145    // We start with 1.0 so that the implicit bit for x86 long doubles146    // is set.147    FPBits bits(FloatType(1.0));148    bits.set_biased_exponent(BIASED_EXPONENT_LIMIT);149    bits.set_sign(Sign::NEG);150    bits.set_mantissa(0);151 152    FloatType x = bits.get_val();153    long mpfr_result;154    bool erangeflag = mpfr::round_to_long(x, mpfr_result);155    ASSERT_FALSE(erangeflag);156    test_one_input(func, x, mpfr_result, false);157  }158 159  void testRoundNumbers(RoundToIntegerFunc func) {160    if (TestModes) {161      for (int mode : ROUNDING_MODES) {162        LIBC_NAMESPACE::fputil::set_round(mode);163        do_round_numbers_test(func);164      }165    } else {166      do_round_numbers_test(func);167    }168  }169 170  void do_fractions_test(RoundToIntegerFunc func, int mode) {171    constexpr FloatType FRACTIONS[] = {172        FloatType(0.5),    FloatType(-0.5),  FloatType(0.115),173        FloatType(-0.115), FloatType(0.715), FloatType(-0.715),174    };175    for (FloatType x : FRACTIONS) {176      long mpfr_long_result;177      bool erangeflag;178      if (TestModes)179        erangeflag = mpfr::round_to_long(x, to_mpfr_rounding_mode(mode),180                                         mpfr_long_result);181      else182        erangeflag = mpfr::round_to_long(x, mpfr_long_result);183      ASSERT_FALSE(erangeflag);184      IntType mpfr_result = mpfr_long_result;185      test_one_input(func, x, mpfr_result, false);186    }187  }188 189  void testFractions(RoundToIntegerFunc func) {190    if (TestModes) {191      for (int mode : ROUNDING_MODES) {192        LIBC_NAMESPACE::fputil::set_round(mode);193        do_fractions_test(func, mode);194      }195    } else {196      // Passing 0 for mode has no effect as it is not used in doFractionsTest197      // when `TestModes` is false;198      do_fractions_test(func, 0);199    }200  }201 202  void testIntegerOverflow(RoundToIntegerFunc func) {203    // This function compares with an equivalent MPFR function which rounds204    // floating point numbers to long values. There is no MPFR function to205    // round to long long or wider integer values. So, we will peform the206    // comparisons in this function only if the width of IntType less than equal207    // to that of long.208    if (sizeof(IntType) > sizeof(long))209      return;210 211    constexpr int EXPONENT_LIMIT = sizeof(IntType) * 8 - 1;212    constexpr int BIASED_EXPONENT_LIMIT = EXPONENT_LIMIT + FPBits::EXP_BIAS;213    if (BIASED_EXPONENT_LIMIT > FPBits::MAX_BIASED_EXPONENT)214      return;215    // We start with 1.0 so that the implicit bit for x86 long doubles216    // is set.217    FPBits bits(FloatType(1.0));218    bits.set_biased_exponent(BIASED_EXPONENT_LIMIT);219    bits.set_sign(Sign::NEG);220    bits.set_mantissa(FPBits::FRACTION_MASK);221 222    FloatType x = bits.get_val();223    if (TestModes) {224      for (int m : ROUNDING_MODES) {225        LIBC_NAMESPACE::fputil::set_round(m);226        long mpfr_long_result;227        bool erangeflag =228            mpfr::round_to_long(x, to_mpfr_rounding_mode(m), mpfr_long_result);229        ASSERT_TRUE(erangeflag);230        test_one_input(func, x, INTEGER_MIN, true);231      }232    } else {233      long mpfr_long_result;234      bool erangeflag = mpfr::round_to_long(x, mpfr_long_result);235      ASSERT_TRUE(erangeflag);236      test_one_input(func, x, INTEGER_MIN, true);237    }238  }239 240  void testSubnormalRange(RoundToIntegerFunc func) {241    constexpr int COUNT = 1'000'001;242    constexpr StorageType STEP = LIBC_NAMESPACE::cpp::max(243        static_cast<StorageType>((MAX_SUBNORMAL - MIN_SUBNORMAL) / COUNT),244        StorageType(1));245    for (StorageType i = MIN_SUBNORMAL; i <= MAX_SUBNORMAL; i += STEP) {246      FloatType x = FPBits(i).get_val();247      if (x == FloatType(0.0))248        continue;249      // All subnormal numbers should round to zero.250      if (TestModes) {251        if (x > 0) {252          LIBC_NAMESPACE::fputil::set_round(FE_UPWARD);253          test_one_input(func, x, IntType(1), false);254          LIBC_NAMESPACE::fputil::set_round(FE_DOWNWARD);255          test_one_input(func, x, IntType(0), false);256          LIBC_NAMESPACE::fputil::set_round(FE_TOWARDZERO);257          test_one_input(func, x, IntType(0), false);258          LIBC_NAMESPACE::fputil::set_round(FE_TONEAREST);259          test_one_input(func, x, IntType(0), false);260        } else {261          LIBC_NAMESPACE::fputil::set_round(FE_UPWARD);262          test_one_input(func, x, IntType(0), false);263          LIBC_NAMESPACE::fputil::set_round(FE_DOWNWARD);264          test_one_input(func, x, IntType(-1), false);265          LIBC_NAMESPACE::fputil::set_round(FE_TOWARDZERO);266          test_one_input(func, x, IntType(0), false);267          LIBC_NAMESPACE::fputil::set_round(FE_TONEAREST);268          test_one_input(func, x, IntType(0), false);269        }270      } else {271        test_one_input(func, x, 0L, false);272      }273    }274  }275 276  void testNormalRange(RoundToIntegerFunc func) {277    // This function compares with an equivalent MPFR function which rounds278    // floating point numbers to long values. There is no MPFR function to279    // round to long long or wider integer values. So, we will peform the280    // comparisons in this function only if the width of IntType less than equal281    // to that of long.282    if (sizeof(IntType) > sizeof(long))283      return;284 285    constexpr int COUNT = 1'000'001;286    constexpr StorageType STEP = LIBC_NAMESPACE::cpp::max(287        static_cast<StorageType>((MAX_NORMAL - MIN_NORMAL) / COUNT),288        StorageType(1));289    for (StorageType i = MIN_NORMAL; i <= MAX_NORMAL; i += STEP) {290      FPBits xbits(i);291      FloatType x = xbits.get_val();292      // In normal range on x86 platforms, the long double implicit 1 bit can be293      // zero making the numbers NaN. We will skip them.294      if (xbits.is_nan())295        continue;296 297      if (TestModes) {298        for (int m : ROUNDING_MODES) {299          long mpfr_long_result;300          bool erangeflag = mpfr::round_to_long(x, to_mpfr_rounding_mode(m),301                                                mpfr_long_result);302          IntType mpfr_result = mpfr_long_result;303          LIBC_NAMESPACE::fputil::set_round(m);304          if (erangeflag)305            test_one_input(func, x, x > 0 ? INTEGER_MAX : INTEGER_MIN, true);306          else307            test_one_input(func, x, mpfr_result, false);308        }309      } else {310        long mpfr_long_result;311        bool erangeflag = mpfr::round_to_long(x, mpfr_long_result);312        IntType mpfr_result = mpfr_long_result;313        if (erangeflag)314          test_one_input(func, x, x > 0 ? INTEGER_MAX : INTEGER_MIN, true);315        else316          test_one_input(func, x, mpfr_result, false);317      }318    }319  }320};321 322#define LIST_ROUND_TO_INTEGER_TESTS_HELPER(FloatType, IntType, func,           \323                                           TestModes)                          \324  using LlvmLibcRoundToIntegerTest =                                           \325      RoundToIntegerTestTemplate<FloatType, IntType, TestModes>;               \326  TEST_F(LlvmLibcRoundToIntegerTest, InfinityAndNaN) {                         \327    testInfinityAndNaN(&func);                                                 \328  }                                                                            \329  TEST_F(LlvmLibcRoundToIntegerTest, RoundNumbers) {                           \330    testRoundNumbers(&func);                                                   \331  }                                                                            \332  TEST_F(LlvmLibcRoundToIntegerTest, Fractions) { testFractions(&func); }      \333  TEST_F(LlvmLibcRoundToIntegerTest, IntegerOverflow) {                        \334    testIntegerOverflow(&func);                                                \335  }                                                                            \336  TEST_F(LlvmLibcRoundToIntegerTest, SubnormalRange) {                         \337    testSubnormalRange(&func);                                                 \338  }                                                                            \339  TEST_F(LlvmLibcRoundToIntegerTest, NormalRange) { testNormalRange(&func); }340 341#define LIST_ROUND_TO_INTEGER_TESTS(FloatType, IntType, func)                  \342  LIST_ROUND_TO_INTEGER_TESTS_HELPER(FloatType, IntType, func, false)343 344#define LIST_ROUND_TO_INTEGER_TESTS_WITH_MODES(FloatType, IntType, func)       \345  LIST_ROUND_TO_INTEGER_TESTS_HELPER(FloatType, IntType, func, true)346 347#endif // LLVM_LIBC_TEST_SRC_MATH_ROUNDTOINTEGERTEST_H348