1 //===-- Collection of utils for implementing math functions -----*- 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-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #ifndef LLVM_LIBC_SRC_MATH_MATH_UTILS_H
10 #define LLVM_LIBC_SRC_MATH_MATH_UTILS_H
11 
12 #include "src/__support/CPP/Bit.h"
13 #include "src/__support/CPP/TypeTraits.h"
14 #include "src/__support/common.h"
15 #include <errno.h>
16 #include <math.h>
17 
18 #include <stdint.h>
19 
20 namespace __llvm_libc {
21 
as_uint32_bits(float x)22 static inline uint32_t as_uint32_bits(float x) {
23   return __llvm_libc::bit_cast<uint32_t>(x);
24 }
25 
as_uint64_bits(double x)26 static inline uint64_t as_uint64_bits(double x) {
27   return __llvm_libc::bit_cast<uint64_t>(x);
28 }
29 
as_float(uint32_t x)30 static inline float as_float(uint32_t x) {
31   return __llvm_libc::bit_cast<float>(x);
32 }
33 
as_double(uint64_t x)34 static inline double as_double(uint64_t x) {
35   return __llvm_libc::bit_cast<double>(x);
36 }
37 
top12_bits(float x)38 static inline uint32_t top12_bits(float x) { return as_uint32_bits(x) >> 20; }
39 
top12_bits(double x)40 static inline uint32_t top12_bits(double x) { return as_uint64_bits(x) >> 52; }
41 
42 // Values to trigger underflow and overflow.
43 template <typename T> struct XFlowValues;
44 
45 template <> struct XFlowValues<float> {
46   static const float OVERFLOW_VALUE;
47   static const float UNDERFLOW_VALUE;
48   static const float MAY_UNDERFLOW_VALUE;
49 };
50 
51 template <> struct XFlowValues<double> {
52   static const double OVERFLOW_VALUE;
53   static const double UNDERFLOW_VALUE;
54   static const double MAY_UNDERFLOW_VALUE;
55 };
56 
57 template <typename T> static inline T with_errno(T x, int err) {
58   if (math_errhandling & MATH_ERRNO)
59     errno = err;
60   return x;
61 }
62 
63 template <typename T> static inline void force_eval(T x) {
64   volatile T y UNUSED = x;
65 }
66 
67 template <typename T> static inline T opt_barrier(T x) {
68   volatile T y = x;
69   return y;
70 }
71 
72 template <typename T> struct IsFloatOrDouble {
73   static constexpr bool
74       Value = // NOLINT so that this Value can match the ones for IsSame
75       cpp::IsSame<T, float>::Value || cpp::IsSame<T, double>::Value;
76 };
77 
78 template <typename T>
79 using EnableIfFloatOrDouble = cpp::EnableIfType<IsFloatOrDouble<T>::Value, int>;
80 
81 template <typename T, EnableIfFloatOrDouble<T> = 0>
82 T xflow(uint32_t sign, T y) {
83   // Underflow happens when two extremely small values are multiplied.
84   // Likewise, overflow happens when two large values are multiplied.
85   y = opt_barrier(sign ? -y : y) * y;
86   return with_errno(y, ERANGE);
87 }
88 
89 template <typename T, EnableIfFloatOrDouble<T> = 0> T overflow(uint32_t sign) {
90   return xflow(sign, XFlowValues<T>::OVERFLOW_VALUE);
91 }
92 
93 template <typename T, EnableIfFloatOrDouble<T> = 0> T underflow(uint32_t sign) {
94   return xflow(sign, XFlowValues<T>::UNDERFLOW_VALUE);
95 }
96 
97 template <typename T, EnableIfFloatOrDouble<T> = 0>
98 T may_underflow(uint32_t sign) {
99   return xflow(sign, XFlowValues<T>::MAY_UNDERFLOW_VALUE);
100 }
101 
102 template <typename T, EnableIfFloatOrDouble<T> = 0>
103 static inline constexpr float invalid(T x) {
104   T y = (x - x) / (x - x);
105   return isnan(x) ? y : with_errno(y, EDOM);
106 }
107 
108 } // namespace __llvm_libc
109 
110 #endif // LLVM_LIBC_SRC_MATH_MATH_UTILS_H
111