blob: 790449cc0080c857c53e316a238eb8193fafeb56 [file]
//===-- Abstract class for bit manipulation of float numbers. ---*- 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_FPBITS_H
#define LLVM_LIBC_SRC___SUPPORT_FPUTIL_FPBITS_H
#include "src/__support/CPP/bit.h"
#include "src/__support/CPP/type_traits.h"
#include "src/__support/UInt128.h"
#include "src/__support/common.h"
#include "src/__support/macros/attributes.h" // LIBC_INLINE
#include "FloatProperties.h"
#include <stdint.h>
namespace LIBC_NAMESPACE {
namespace fputil {
namespace internal {
// This is a temporary class to unify common methods and properties between
// FPBits and FPBits<long double>.
template <FPType fp_type> struct FPBitsCommon : private FPProperties<fp_type> {
using UP = FPProperties<fp_type>;
using typename UP::StorageType;
using UP::TOTAL_LEN;
protected:
using UP::EXP_SIG_MASK;
using UP::QUIET_NAN_MASK;
public:
using UP::EXP_BIAS;
using UP::EXP_LEN;
using UP::EXP_MASK;
using UP::EXP_MASK_SHIFT;
using UP::FP_MASK;
using UP::FRACTION_LEN;
using UP::FRACTION_MASK;
using UP::SIGN_MASK;
// Reinterpreting bits as an integer value and interpreting the bits of an
// integer value as a floating point value is used in tests. So, a convenient
// type is provided for such reinterpretations.
StorageType bits;
LIBC_INLINE constexpr FPBitsCommon() : bits(0) {}
LIBC_INLINE explicit constexpr FPBitsCommon(StorageType bits) : bits(bits) {}
LIBC_INLINE constexpr void set_mantissa(StorageType mantVal) {
mantVal &= FRACTION_MASK;
bits &= ~FRACTION_MASK;
bits |= mantVal;
}
LIBC_INLINE constexpr StorageType get_mantissa() const {
return bits & FRACTION_MASK;
}
LIBC_INLINE constexpr void set_sign(bool signVal) {
if (get_sign() != signVal)
bits ^= SIGN_MASK;
}
LIBC_INLINE constexpr bool get_sign() const {
return (bits & SIGN_MASK) != 0;
}
LIBC_INLINE constexpr void set_biased_exponent(StorageType biased) {
// clear exponent bits
bits &= ~EXP_MASK;
// set exponent bits
bits |= (biased << EXP_MASK_SHIFT) & EXP_MASK;
}
LIBC_INLINE constexpr uint16_t get_biased_exponent() const {
return uint16_t((bits & EXP_MASK) >> EXP_MASK_SHIFT);
}
LIBC_INLINE constexpr int get_exponent() const {
return int(get_biased_exponent()) - EXP_BIAS;
}
// If the number is subnormal, the exponent is treated as if it were the
// minimum exponent for a normal number. This is to keep continuity between
// the normal and subnormal ranges, but it causes problems for functions where
// values are calculated from the exponent, since just subtracting the bias
// will give a slightly incorrect result. Additionally, zero has an exponent
// of zero, and that should actually be treated as zero.
LIBC_INLINE constexpr int get_explicit_exponent() const {
const int biased_exp = int(get_biased_exponent());
if (is_zero()) {
return 0;
} else if (biased_exp == 0) {
return 1 - EXP_BIAS;
} else {
return biased_exp - EXP_BIAS;
}
}
LIBC_INLINE constexpr StorageType uintval() const { return bits & FP_MASK; }
LIBC_INLINE constexpr bool is_zero() const {
return (bits & EXP_SIG_MASK) == 0;
}
};
} // namespace internal
// A generic class to represent single precision, double precision, and quad
// precision IEEE 754 floating point formats.
// On most platforms, the 'float' type corresponds to single precision floating
// point numbers, the 'double' type corresponds to double precision floating
// point numers, and the 'long double' type corresponds to the quad precision
// floating numbers. On x86 platforms however, the 'long double' type maps to
// an x87 floating point format. This format is an IEEE 754 extension format.
// It is handled as an explicit specialization of this class.
template <typename T>
struct FPBits : public internal::FPBitsCommon<get_fp_type<T>()> {
static_assert(cpp::is_floating_point_v<T>,
"FPBits instantiated with invalid type.");
using UP = internal::FPBitsCommon<get_fp_type<T>()>;
using StorageType = typename UP::StorageType;
using UP::bits;
private:
using UP::EXP_SIG_MASK;
using UP::QUIET_NAN_MASK;
public:
using UP::EXP_BIAS;
using UP::EXP_LEN;
using UP::EXP_MASK;
using UP::EXP_MASK_SHIFT;
using UP::FRACTION_LEN;
using UP::FRACTION_MASK;
using UP::SIGN_MASK;
using UP::TOTAL_LEN;
using UP::get_biased_exponent;
using UP::is_zero;
// The function return mantissa with the implicit bit set iff the current
// value is a valid normal number.
LIBC_INLINE constexpr StorageType get_explicit_mantissa() {
return ((get_biased_exponent() > 0 && !is_inf_or_nan())
? (FRACTION_MASK + 1)
: 0) |
(FRACTION_MASK & bits);
}
static constexpr int MAX_BIASED_EXPONENT = (1 << EXP_LEN) - 1;
static constexpr StorageType MIN_SUBNORMAL = StorageType(1);
static constexpr StorageType MAX_SUBNORMAL = FRACTION_MASK;
static constexpr StorageType MIN_NORMAL = (StorageType(1) << FRACTION_LEN);
static constexpr StorageType MAX_NORMAL =
((StorageType(MAX_BIASED_EXPONENT) - 1) << FRACTION_LEN) | MAX_SUBNORMAL;
// We don't want accidental type promotions/conversions, so we require exact
// type match.
template <typename XType, cpp::enable_if_t<cpp::is_same_v<T, XType>, int> = 0>
LIBC_INLINE constexpr explicit FPBits(XType x)
: UP(cpp::bit_cast<StorageType>(x)) {}
template <typename XType,
cpp::enable_if_t<cpp::is_same_v<XType, StorageType>, int> = 0>
LIBC_INLINE constexpr explicit FPBits(XType x) : UP(x) {}
LIBC_INLINE constexpr FPBits() : UP() {}
LIBC_INLINE constexpr void set_val(T value) {
bits = cpp::bit_cast<StorageType>(value);
}
LIBC_INLINE constexpr T get_val() const { return cpp::bit_cast<T>(bits); }
LIBC_INLINE constexpr explicit operator T() const { return get_val(); }
LIBC_INLINE constexpr bool is_inf() const {
return (bits & EXP_SIG_MASK) == EXP_MASK;
}
LIBC_INLINE constexpr bool is_nan() const {
return (bits & EXP_SIG_MASK) > EXP_MASK;
}
LIBC_INLINE constexpr bool is_quiet_nan() const {
return (bits & EXP_SIG_MASK) == (EXP_MASK | QUIET_NAN_MASK);
}
LIBC_INLINE constexpr bool is_inf_or_nan() const {
return (bits & EXP_MASK) == EXP_MASK;
}
LIBC_INLINE constexpr FPBits abs() const {
return FPBits(bits & EXP_SIG_MASK);
}
LIBC_INLINE static constexpr T zero(bool sign = false) {
return FPBits(sign ? SIGN_MASK : StorageType(0)).get_val();
}
LIBC_INLINE static constexpr T neg_zero() { return zero(true); }
LIBC_INLINE static constexpr T inf(bool sign = false) {
return FPBits((sign ? SIGN_MASK : StorageType(0)) | EXP_MASK).get_val();
}
LIBC_INLINE static constexpr T neg_inf() { return inf(true); }
LIBC_INLINE static constexpr T min_normal() {
return FPBits(MIN_NORMAL).get_val();
}
LIBC_INLINE static constexpr T max_normal() {
return FPBits(MAX_NORMAL).get_val();
}
LIBC_INLINE static constexpr T min_denormal() {
return FPBits(MIN_SUBNORMAL).get_val();
}
LIBC_INLINE static constexpr T max_denormal() {
return FPBits(MAX_SUBNORMAL).get_val();
}
LIBC_INLINE static constexpr T build_nan(StorageType v) {
FPBits<T> bits(inf());
bits.set_mantissa(v);
return T(bits);
}
LIBC_INLINE static constexpr T build_quiet_nan(StorageType v) {
return build_nan(QUIET_NAN_MASK | v);
}
// The function convert integer number and unbiased exponent to proper float
// T type:
// Result = number * 2^(ep+1 - exponent_bias)
// Be careful!
// 1) "ep" is raw exponent value.
// 2) The function add to +1 to ep for seamless normalized to denormalized
// transition.
// 3) The function did not check exponent high limit.
// 4) "number" zero value is not processed correctly.
// 5) Number is unsigned, so the result can be only positive.
LIBC_INLINE static constexpr FPBits<T> make_value(StorageType number,
int ep) {
FPBits<T> result;
// offset: +1 for sign, but -1 for implicit first bit
int lz = cpp::countl_zero(number) - EXP_LEN;
number <<= lz;
ep -= lz;
if (LIBC_LIKELY(ep >= 0)) {
// Implicit number bit will be removed by mask
result.set_mantissa(number);
result.set_biased_exponent(ep + 1);
} else {
result.set_mantissa(number >> -ep);
}
return result;
}
LIBC_INLINE static constexpr FPBits<T>
create_value(bool sign, StorageType biased_exp, StorageType mantissa) {
FPBits<T> result;
result.set_sign(sign);
result.set_biased_exponent(biased_exp);
result.set_mantissa(mantissa);
return result;
}
};
} // namespace fputil
} // namespace LIBC_NAMESPACE
#ifdef LIBC_LONG_DOUBLE_IS_X86_FLOAT80
#include "x86_64/LongDoubleBits.h"
#endif
#endif // LLVM_LIBC_SRC___SUPPORT_FPUTIL_FPBITS_H