brintos

brintos / llvm-project-archived public Read only

0
0
Text · 4.9 KiB · 8b0e900 Raw
148 lines · plain
1//===----------------------------------------------------------------------===//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/// \file10/// This file contains the definition of numeric utilities, including functions11/// to compute ULP distance and traits for floating-point types.12///13//===----------------------------------------------------------------------===//14 15#ifndef MATHTEST_NUMERICS_HPP16#define MATHTEST_NUMERICS_HPP17 18#include "mathtest/Support.hpp"19#include "mathtest/TypeExtras.hpp"20 21// These headers are in the shared LLVM-libc header library22#include "shared/fp_bits.h"23#include "shared/sign.h"24 25#include <climits>26#include <cstdint>27#include <limits>28#include <math.h>29#include <type_traits>30 31namespace mathtest {32 33template <typename FloatType>34using FPBits = LIBC_NAMESPACE::shared::FPBits<FloatType>;35 36using Sign = LIBC_NAMESPACE::shared::Sign;37 38//===----------------------------------------------------------------------===//39// Type Traits40//===----------------------------------------------------------------------===//41 42template <typename T> struct IsFloatingPoint : std::is_floating_point<T> {};43 44template <> struct IsFloatingPoint<float16> : std::true_type {};45 46template <typename T>47inline constexpr bool IsFloatingPoint_v // NOLINT(readability-identifier-naming)48    = IsFloatingPoint<T>::value;49 50template <typename T> struct StorageTypeOf {51private:52  static constexpr auto getStorageType() noexcept {53    if constexpr (IsFloatingPoint_v<T>)54      return TypeIdentityOf<typename FPBits<T>::StorageType>{};55    else if constexpr (std::is_unsigned_v<T>)56      return TypeIdentityOf<T>{};57    else if constexpr (std::is_signed_v<T>)58      return TypeIdentityOf<std::make_unsigned_t<T>>{};59    else60      static_assert(!std::is_same_v<T, T>, "Unsupported type");61  }62 63public:64  using type = typename decltype(getStorageType())::type;65};66 67template <typename T> using StorageTypeOf_t = typename StorageTypeOf<T>::type;68 69//===----------------------------------------------------------------------===//70// Numeric Functions71//===----------------------------------------------------------------------===//72 73template <typename T> [[nodiscard]] constexpr T getMinOrNegInf() noexcept {74  if constexpr (IsFloatingPoint_v<T>) {75    // All currently supported floating-point types have infinity76    return FPBits<T>::inf(Sign::NEG).get_val();77  } else {78    static_assert(std::is_integral_v<T>,79                  "Type T must be an integral or floating-point type");80 81    return std::numeric_limits<T>::lowest();82  }83}84 85template <typename T> [[nodiscard]] constexpr T getMaxOrInf() noexcept {86  if constexpr (IsFloatingPoint_v<T>) {87    // All currently supported floating-point types have infinity88    return FPBits<T>::inf(Sign::POS).get_val();89  } else {90    static_assert(std::is_integral_v<T>,91                  "Type T must be an integral or floating-point type");92 93    return std::numeric_limits<T>::max();94  }95}96 97template <typename FloatType>98[[nodiscard]] uint64_t computeUlpDistance(FloatType X, FloatType Y) noexcept {99  static_assert(IsFloatingPoint_v<FloatType>,100                "FloatType must be a floating-point type");101  using FPBits = FPBits<FloatType>;102  using StorageType = typename FPBits::StorageType;103 104  const FPBits XBits(X);105  const FPBits YBits(Y);106 107  if (X == Y) {108    if (XBits.sign() != YBits.sign()) [[unlikely]] {109      // When X == Y, different sign bits imply that X and Y are +0.0 and -0.0110      // (in any order). Since we want to treat them as unequal in the context111      // of accuracy testing of mathematical functions, we return the smallest112      // non-zero value.113      return 1;114    }115    return 0;116  }117 118  const bool XIsNaN = XBits.is_nan();119  const bool YIsNaN = YBits.is_nan();120 121  if (XIsNaN && YIsNaN)122    return 0;123 124  if (XIsNaN || YIsNaN)125    return std::numeric_limits<uint64_t>::max();126 127  constexpr StorageType SignMask = FPBits::SIGN_MASK;128 129  // Linearise FloatType values into an ordered unsigned space. Let a and b130  // be bits(x), bits(y), respectively, where x and y are FloatType values.131  //  * The mapping is monotonic: x >= y if, and only if, map(a) >= map(b).132  //  * The difference |map(a) − map(b)| equals the number of std::nextafter133  //    steps between a and b within the same type.134  auto MapToOrderedUnsigned = [](FPBits Bits) {135    const StorageType Unsigned = Bits.uintval();136    return (Unsigned & SignMask) ? SignMask - (Unsigned - SignMask)137                                 : SignMask + Unsigned;138  };139 140  const StorageType MappedX = MapToOrderedUnsigned(XBits);141  const StorageType MappedY = MapToOrderedUnsigned(YBits);142  return static_cast<uint64_t>(MappedX > MappedY ? MappedX - MappedY143                                                 : MappedY - MappedX);144}145} // namespace mathtest146 147#endif // MATHTEST_NUMERICS_HPP148