LongDoubleBitsX86.h 4.66 KB
//===-- Bit representation of x86 long double 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_UTILS_FPUTIL_LONG_DOUBLE_BITS_X86_H
#define LLVM_LIBC_UTILS_FPUTIL_LONG_DOUBLE_BITS_X86_H

#include "FPBits.h"

#include <stdint.h>

namespace __llvm_libc {
namespace fputil {

template <> struct MantissaWidth<long double> {
  static constexpr unsigned value = 63;
};

template <unsigned Width> struct Padding;

// i386 padding.
template <> struct Padding<4> { static constexpr unsigned value = 16; };

// x86_64 padding.
template <> struct Padding<8> { static constexpr unsigned value = 48; };

template <> struct __attribute__((packed)) FPBits<long double> {
  using UIntType = __uint128_t;

  static constexpr int exponentBias = 0x3FFF;
  static constexpr int maxExponent = 0x7FFF;
  static constexpr UIntType minSubnormal = UIntType(1);
  // Subnormal numbers include the implicit bit in x86 long double formats.
  static constexpr UIntType maxSubnormal =
      (UIntType(1) << (MantissaWidth<long double>::value + 1)) - 1;
  static constexpr UIntType minNormal =
      (UIntType(3) << MantissaWidth<long double>::value);
  static constexpr UIntType maxNormal =
      ((UIntType(maxExponent) - 1) << (MantissaWidth<long double>::value + 1)) |
      (UIntType(1) << MantissaWidth<long double>::value) | maxSubnormal;

  UIntType mantissa : MantissaWidth<long double>::value;
  uint8_t implicitBit : 1;
  uint16_t exponent : ExponentWidth<long double>::value;
  uint8_t sign : 1;
  uint64_t padding : Padding<sizeof(uintptr_t)>::value;

  template <typename XType,
            cpp::EnableIfType<cpp::IsSame<long double, XType>::Value, int> = 0>
  explicit FPBits<long double>(XType x) {
    *this = *reinterpret_cast<FPBits<long double> *>(&x);
  }

  operator long double() { return *reinterpret_cast<long double *>(this); }

  int getExponent() const {
    if (exponent == 0)
      return int(1) - exponentBias;
    return int(exponent) - exponentBias;
  }

  bool isZero() const {
    return exponent == 0 && mantissa == 0 && implicitBit == 0;
  }

  bool isInf() const {
    return exponent == maxExponent && mantissa == 0 && implicitBit == 1;
  }

  bool isNaN() const {
    if (exponent == maxExponent) {
      return (implicitBit == 0) || mantissa != 0;
    } else if (exponent != 0) {
      return implicitBit == 0;
    }
    return false;
  }

  bool isInfOrNaN() const {
    return (exponent == maxExponent) || (exponent != 0 && implicitBit == 0);
  }

  // Methods below this are used by tests.

  template <typename XType,
            cpp::EnableIfType<cpp::IsSame<UIntType, XType>::Value, int> = 0>
  explicit FPBits<long double>(XType x) {
    // The last 4 bytes of v are ignored in case of i386.
    *this = *reinterpret_cast<FPBits<long double> *>(&x);
  }

  UIntType bitsAsUInt() const {
    // We cannot just return the bits as is as it will lead to reading
    // out of bounds in case of i386. So, we first copy the wider value
    // before returning the value. This makes the last 4 bytes are always
    // zero in case i386.
    UIntType result = UIntType(0);
    *reinterpret_cast<FPBits<long double> *>(&result) = *this;

    // Even though we zero out |result| before copying the long double value,
    // there can be garbage bits in the padding. So, we zero the padding bits
    // in |result|.
    static constexpr UIntType mask =
        (UIntType(1) << (sizeof(long double) * 8 -
                         Padding<sizeof(uintptr_t)>::value)) -
        1;
    return result & mask;
  }

  static FPBits<long double> zero() { return FPBits<long double>(0.0l); }

  static FPBits<long double> negZero() {
    FPBits<long double> bits(0.0l);
    bits.sign = 1;
    return bits;
  }

  static FPBits<long double> inf() {
    FPBits<long double> bits(0.0l);
    bits.exponent = maxExponent;
    bits.implicitBit = 1;
    return bits;
  }

  static FPBits<long double> negInf() {
    FPBits<long double> bits(0.0l);
    bits.exponent = maxExponent;
    bits.implicitBit = 1;
    bits.sign = 1;
    return bits;
  }

  static long double buildNaN(UIntType v) {
    FPBits<long double> bits(0.0l);
    bits.exponent = maxExponent;
    bits.implicitBit = 1;
    bits.mantissa = v;
    return bits;
  }
};

static_assert(
    sizeof(FPBits<long double>) == sizeof(long double),
    "Internal long double representation does not match the machine format.");

} // namespace fputil
} // namespace __llvm_libc

#endif // LLVM_LIBC_UTILS_FPUTIL_LONG_DOUBLE_BITS_X86_H