summaryrefslogtreecommitdiff
path: root/libc/src/math/generic/math_utils.h
blob: 0399f7d0adfff2c6cf9830456ce525b056ae9e09 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
//===-- Collection of utils for implementing math functions -----*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_LIBC_SRC_MATH_MATH_UTILS_H
#define LLVM_LIBC_SRC_MATH_MATH_UTILS_H

#include "src/__support/CPP/bit.h"
#include "src/__support/CPP/type_traits.h"
#include "src/__support/common.h"
#include "src/errno/libc_errno.h"

#include <math.h>

#include <stdint.h>

namespace __llvm_libc {

LIBC_INLINE uint32_t as_uint32_bits(float x) {
  return cpp::bit_cast<uint32_t>(x);
}

LIBC_INLINE uint64_t as_uint64_bits(double x) {
  return cpp::bit_cast<uint64_t>(x);
}

LIBC_INLINE float as_float(uint32_t x) { return cpp::bit_cast<float>(x); }

LIBC_INLINE double as_double(uint64_t x) { return cpp::bit_cast<double>(x); }

LIBC_INLINE uint32_t top12_bits(float x) { return as_uint32_bits(x) >> 20; }

LIBC_INLINE uint32_t top12_bits(double x) { return as_uint64_bits(x) >> 52; }

// Values to trigger underflow and overflow.
template <typename T> struct XFlowValues;

template <> struct XFlowValues<float> {
  static const float OVERFLOW_VALUE;
  static const float UNDERFLOW_VALUE;
  static const float MAY_UNDERFLOW_VALUE;
};

template <> struct XFlowValues<double> {
  static const double OVERFLOW_VALUE;
  static const double UNDERFLOW_VALUE;
  static const double MAY_UNDERFLOW_VALUE;
};

template <typename T> LIBC_INLINE T with_errno(T x, int err) {
  if (math_errhandling & MATH_ERRNO)
    libc_errno = err;
  return x;
}

template <typename T> LIBC_INLINE void force_eval(T x) {
  volatile T y LIBC_UNUSED = x;
}

template <typename T> LIBC_INLINE T opt_barrier(T x) {
  volatile T y = x;
  return y;
}

template <typename T> struct IsFloatOrDouble {
  static constexpr bool
      Value = // NOLINT so that this Value can match the ones for IsSame
      cpp::is_same_v<T, float> || cpp::is_same_v<T, double>;
};

template <typename T>
using EnableIfFloatOrDouble = cpp::enable_if_t<IsFloatOrDouble<T>::Value, int>;

template <typename T, EnableIfFloatOrDouble<T> = 0>
T xflow(uint32_t sign, T y) {
  // Underflow happens when two extremely small values are multiplied.
  // Likewise, overflow happens when two large values are multiplied.
  y = opt_barrier(sign ? -y : y) * y;
  return with_errno(y, ERANGE);
}

template <typename T, EnableIfFloatOrDouble<T> = 0> T overflow(uint32_t sign) {
  return xflow(sign, XFlowValues<T>::OVERFLOW_VALUE);
}

template <typename T, EnableIfFloatOrDouble<T> = 0> T underflow(uint32_t sign) {
  return xflow(sign, XFlowValues<T>::UNDERFLOW_VALUE);
}

template <typename T, EnableIfFloatOrDouble<T> = 0>
T may_underflow(uint32_t sign) {
  return xflow(sign, XFlowValues<T>::MAY_UNDERFLOW_VALUE);
}

template <typename T, EnableIfFloatOrDouble<T> = 0>
LIBC_INLINE constexpr float invalid(T x) {
  T y = (x - x) / (x - x);
  return isnan(x) ? y : with_errno(y, EDOM);
}

} // namespace __llvm_libc

#endif // LLVM_LIBC_SRC_MATH_MATH_UTILS_H