blob: 0399f7d0adfff2c6cf9830456ce525b056ae9e09 [file] [log] [blame]
//===-- 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