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