blob: c0bc40cd6c1aaef899d14735e82117b36054257c [file]
//===-- Definition for Float80 data type ------------------------*- 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___SUPPORT_FPUTIL_FLOAT80_H
#define LLVM_LIBC_SRC___SUPPORT_FPUTIL_FLOAT80_H
#include "hdr/stdint_proxy.h"
#include "src/__support/CPP/type_traits.h"
#include "src/__support/FPUtil/cast.h"
#include "src/__support/FPUtil/comparison_operations.h"
#include "src/__support/FPUtil/dyadic_float.h"
#include "src/__support/FPUtil/float128.h"
#include "src/__support/FPUtil/generic/add_sub.h"
#include "src/__support/FPUtil/generic/div.h"
#include "src/__support/FPUtil/generic/mul.h"
#include "src/__support/macros/attributes.h"
#include "src/__support/macros/config.h"
#include "src/__support/uint128.h"
namespace LIBC_NAMESPACE_DECL {
namespace fputil {
struct Float80 {
#if __SIZEOF_LONG_DOUBLE__ == 12
UInt<96> bits;
#else
UInt128 bits;
#endif
LIBC_INLINE Float80() = default;
LIBC_INLINE constexpr Float80(const Float80 &) = default;
LIBC_INLINE constexpr Float80(Float80 &&) = default;
LIBC_INLINE constexpr Float80 &operator=(const Float80 &) = default;
LIBC_INLINE constexpr Float80 &operator=(Float80 &&) = default;
// Floating point type and integer type
template <typename T>
LIBC_INLINE constexpr explicit Float80(T value) : bits(0U) {
if constexpr (cpp::is_floating_point_v<T>) {
bits = fputil::cast<Float80>(value).bits;
} else if constexpr (cpp::is_integral_v<T>) {
Sign sign = Sign::POS;
auto unsigned_value = static_cast<cpp::make_unsigned_t<T>>(value);
if constexpr (cpp::is_signed_v<T>) {
if (value < 0) {
sign = Sign::NEG;
unsigned_value = -unsigned_value;
}
}
fputil::DyadicFloat<FPBits<Float80>::STORAGE_LEN> xd(sign, 0,
unsigned_value);
bits = xd.template as<Float80, /*ShouldSignalExceptions=*/true>().bits;
} else if constexpr (cpp::is_convertible_v<T, Float80>) {
bits = value.operator Float80().bits;
} else {
bits = fputil::cast<Float80>(static_cast<float>(value)).bits;
}
}
template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T> &&
!cpp::is_same_v<T, Float80>,
int> = 0>
LIBC_INLINE LIBC_CONSTEXPR_DEFAULT operator T() const {
return fputil::cast<T>(*this);
}
template <typename T, cpp::enable_if_t<cpp::is_integral_v<T>, int> = 0>
LIBC_INLINE constexpr explicit operator T() const {
constexpr T MIN_T = cpp::numeric_limits<T>::min();
constexpr T MAX_T = cpp::numeric_limits<T>::max();
FPBits<Float80> x_bits(*this);
// Raise FE_INVALID for inf and NaN
if (x_bits.is_inf_or_nan()) {
raise_except_if_required(FE_INVALID);
return x_bits.is_neg() ? MIN_T : MAX_T;
}
int exponent = x_bits.get_explicit_exponent();
constexpr int EXPONENT_LIMIT = cpp::numeric_limits<T>::digits;
if (exponent > EXPONENT_LIMIT) {
raise_except_if_required(FE_INVALID);
return x_bits.is_neg() ? MIN_T : MAX_T;
} else if (exponent == EXPONENT_LIMIT) {
if (x_bits.is_pos() || x_bits.get_mantissa() != 0) {
raise_except_if_required(FE_INVALID);
return x_bits.is_neg() ? MIN_T : MAX_T;
}
}
int x_bits_exp = exponent - FPBits<Float80>::FRACTION_LEN;
// sign * 2^(exp-bias) * mantissa
DyadicFloat<FPBits<Float80>::STORAGE_LEN> xd(
x_bits.sign(), x_bits_exp, x_bits.get_explicit_mantissa());
return static_cast<T>(xd.as_mantissa_type());
}
// unary operator
LIBC_INLINE LIBC_BIT_CAST_CONSTEXPR Float80 operator-() const {
fputil::FPBits<Float80> result(*this);
result.set_sign(result.is_pos() ? Sign::NEG : Sign::POS);
return result.get_val();
}
LIBC_INLINE constexpr Float80 operator+(const Float80 &other) const {
return fputil::generic::add<Float80>(fputil::cast<Float128>(*this),
fputil::cast<Float128>(other));
}
LIBC_INLINE constexpr Float80 operator-(const Float80 &other) const {
return fputil::generic::sub<Float80>(fputil::cast<Float128>(*this),
fputil::cast<Float128>(other));
}
LIBC_INLINE constexpr Float80 operator*(const Float80 &other) const {
return fputil::generic::mul<Float80>(fputil::cast<Float128>(*this),
fputil::cast<Float128>(other));
}
LIBC_INLINE constexpr Float80 operator/(const Float80 &other) const {
return fputil::generic::div<Float80>(fputil::cast<Float128>(*this),
fputil::cast<Float128>(other));
}
// Comparison operators
LIBC_INLINE constexpr bool operator==(const Float80 &other) const {
return fputil::equals(*this, other);
}
LIBC_INLINE constexpr bool operator!=(const Float80 &other) const {
return !fputil::equals(*this, other);
}
LIBC_INLINE constexpr bool operator<(const Float80 &other) const {
return fputil::less_than(*this, other);
}
LIBC_INLINE constexpr bool operator<=(const Float80 &other) const {
return fputil::less_than_or_equals(*this, other);
}
LIBC_INLINE constexpr bool operator>(const Float80 &other) const {
return fputil::greater_than(*this, other);
}
LIBC_INLINE constexpr bool operator>=(const Float80 &other) const {
return fputil::greater_than_or_equals(*this, other);
}
};
} // namespace fputil
} // namespace LIBC_NAMESPACE_DECL
#endif // LLVM_LIBC_SRC___SUPPORT_FPUTIL_Float80_H