13 changes: 11 additions & 2 deletions libc/utils/FPUtil/BasicOperations.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,25 @@
//
//===----------------------------------------------------------------------===//

#include "FloatOperations.h"
#ifndef LLVM_LIBC_UTILS_FPUTIL_BASIC_OPERATIONS_H
#define LLVM_LIBC_UTILS_FPUTIL_BASIC_OPERATIONS_H

#include "FPBits.h"

#include "utils/CPP/TypeTraits.h"

namespace __llvm_libc {
namespace fputil {

template <typename T,
cpp::EnableIfType<cpp::IsFloatingPointType<T>::Value, int> = 0>
static inline T abs(T x) {
return valueFromBits(absBits(x));
FPBits<T> bits(x);
bits.sign = 0;
return T(bits);
}

} // namespace fputil
} // namespace __llvm_libc

#endif // LLVM_LIBC_UTILS_FPUTIL_BASIC_OPERATIONS_H
10 changes: 9 additions & 1 deletion libc/utils/FPUtil/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
if(${LIBC_TARGET_MACHINE} MATCHES "^x86.*")
set(LONG_DOUBLE_HDR LongDoubleBitsX86.h)
else()
set(LONG_DOUBLE_HDR)
endif()

add_header_library(
fputil
HDRS
${LONG_DOUBLE_HDR}
BitPatterns.h
FloatOperations.h
FloatProperties.h
FPBits.h
ManipulationFunctions.h
DEPS
DEPENDS
libc.utils.CPP.standalone_cpp
)
148 changes: 148 additions & 0 deletions libc/utils/FPUtil/FPBits.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
//===-- 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_UTILS_FPUTIL_FP_BITS_H
#define LLVM_LIBC_UTILS_FPUTIL_FP_BITS_H

#include "utils/CPP/TypeTraits.h"

#include <stdint.h>

namespace __llvm_libc {
namespace fputil {

template <typename T> struct MantissaWidth {};
template <> struct MantissaWidth<float> {
static constexpr unsigned value = 23;
};
template <> struct MantissaWidth<double> {
static constexpr unsigned value = 52;
};

template <typename T> struct ExponentWidth {};
template <> struct ExponentWidth<float> {
static constexpr unsigned value = 8;
};
template <> struct ExponentWidth<double> {
static constexpr unsigned value = 11;
};
template <> struct ExponentWidth<long double> {
static constexpr unsigned value = 15;
};

template <typename T> struct FPUIntType {};
template <> struct FPUIntType<float> { using Type = uint32_t; };
template <> struct FPUIntType<double> { using Type = uint64_t; };

#if !(defined(__x86_64__) || defined(__i386__))
// TODO: This has to be extended for visual studio where long double and
// double are equivalent.
template <> struct MantissaWidth<long double> {
static constexpr unsigned value = 112;
};

template <> struct FPUIntType<long double> { using Type = __uint128_t; };
#endif

// 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 __attribute__((packed)) FPBits {
static_assert(cpp::IsFloatingPointType<T>::Value,
"FPBits instantiated with invalid type.");

// 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.
using UIntType = typename FPUIntType<T>::Type;

UIntType mantissa : MantissaWidth<T>::value;
uint16_t exponent : ExponentWidth<T>::value;
uint8_t sign : 1;

static constexpr int exponentBias = (1 << (ExponentWidth<T>::value - 1)) - 1;
static constexpr int maxExponent = (1 << ExponentWidth<T>::value) - 1;

// We don't want accidental type promotions/conversions so we require exact
// type match.
template <typename XType,
cpp::EnableIfType<cpp::IsSame<T, XType>::Value, int> = 0>
explicit FPBits(XType x) {
*this = *reinterpret_cast<FPBits<T> *>(&x);
}

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

int getExponent() const { return int(exponent) - exponentBias; }

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

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

bool isNaN() const { return exponent == maxExponent && mantissa != 0; }

bool isInfOrNaN() const { return exponent == maxExponent; }

// Methods below this are used by tests.
// The to and from integer bits converters are only used in tests. Hence,
// the potential software implementations of UIntType will not slow real
// code.

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<T> *>(&x);
}

UIntType bitsAsUInt() const {
return *reinterpret_cast<const UIntType *>(this);
}

static FPBits<T> zero() { return FPBits(T(0.0)); }

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

static FPBits<T> inf() {
FPBits<T> bits(T(0.0));
bits.exponent = maxExponent;
return bits;
}

static FPBits<T> negInf() {
FPBits<T> bits(T(0.0));
bits.exponent = maxExponent;
bits.sign = 1;
return bits;
}

static T buildNaN(UIntType v) {
FPBits<T> bits(T(0.0));
bits.exponent = maxExponent;
bits.mantissa = v;
return bits;
}
};

} // namespace fputil
} // namespace __llvm_libc

#if defined(__x86_64__) || defined(__i386__)
#include "utils/FPUtil/LongDoubleBitsX86.h"
#endif

#endif // LLVM_LIBC_UTILS_FPUTIL_FP_BITS_H
1 change: 0 additions & 1 deletion libc/utils/FPUtil/FloatOperations.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ static inline int getExponent(T x) {
return getExponentFromBits(valueAsBits(x));
}


} // namespace fputil
} // namespace __llvm_libc

Expand Down
127 changes: 127 additions & 0 deletions libc/utils/FPUtil/LongDoubleBitsX86.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
//===-- 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 "utils/FPUtil/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;

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 { return exponent == maxExponent && mantissa != 0; }

bool isInfOrNaN() const { return exponent == maxExponent; }

// 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;
return result;
}

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
24 changes: 13 additions & 11 deletions libc/utils/FPUtil/NearestIntegerOperations.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#define LLVM_LIBC_UTILS_FPUTIL_NEAREST_INTEGER_OPERATIONS_H

#include "ClassificationFunctions.h"
#include "FPBits.h"
#include "FloatOperations.h"
#include "FloatProperties.h"

Expand All @@ -21,32 +22,33 @@ namespace fputil {
template <typename T,
cpp::EnableIfType<cpp::IsFloatingPointType<T>::Value, int> = 0>
static inline T trunc(T x) {
using Properties = FloatProperties<T>;
using BitsType = typename FloatProperties<T>::BitsType;
FPBits<T> bits(x);

BitsType bits = valueAsBits(x);

// If x is infinity, NaN or zero, return it.
if (bitsAreInfOrNaN(bits) || bitsAreZero(bits))
// If x is infinity or NaN, return it.
// If it is zero also we should return it as is, but the logic
// later in this function takes care of it. But not doing a zero
// check, we improve the run time of non-zero values.
if (bits.isInfOrNaN())
return x;

int exponent = getExponentFromBits(bits);
int exponent = bits.getExponent();

// If the exponent is greater than the most negative mantissa
// exponent, then x is already an integer.
if (exponent >= static_cast<int>(Properties::mantissaWidth))
if (exponent >= static_cast<int>(MantissaWidth<T>::value))
return x;

// If the exponent is such that abs(x) is less than 1, then return 0.
if (exponent <= -1) {
if (Properties::signMask & bits)
if (bits.sign)
return T(-0.0);
else
return T(0.0);
}

uint32_t trimSize = Properties::mantissaWidth - exponent;
return valueFromBits((bits >> trimSize) << trimSize);
int trimSize = MantissaWidth<T>::value - exponent;
bits.mantissa = (bits.mantissa >> trimSize) << trimSize;
return bits;
}

template <typename T,
Expand Down
33 changes: 24 additions & 9 deletions libc/utils/MPFRWrapper/MPFRUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

#include "MPFRUtils.h"

#include "utils/FPUtil/FloatOperations.h"
#include "utils/FPUtil/FPBits.h"

#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
Expand All @@ -17,14 +17,16 @@
#include <stdint.h>
#include <string>

template <typename T> using FPBits = __llvm_libc::fputil::FPBits<T>;

namespace __llvm_libc {
namespace testing {
namespace mpfr {

class MPFRNumber {
// A precision value which allows sufficiently large additional
// precision even compared to double precision floating point values.
static constexpr unsigned int mpfrPrecision = 96;
// precision even compared to quad-precision floating point values.
static constexpr unsigned int mpfrPrecision = 128;

mpfr_t value;

Expand All @@ -48,6 +50,13 @@ class MPFRNumber {
mpfr_set_d(value, x, MPFR_RNDN);
}

template <typename XType,
cpp::EnableIfType<cpp::IsSame<long double, XType>::Value, int> = 0>
explicit MPFRNumber(XType x) {
mpfr_init2(value, mpfrPrecision);
mpfr_set_ld(value, x, MPFR_RNDN);
}

template <typename XType,
cpp::EnableIfType<cpp::IsIntegral<XType>::Value, int> = 0>
explicit MPFRNumber(XType x) {
Expand All @@ -58,7 +67,7 @@ class MPFRNumber {
template <typename XType> MPFRNumber(XType x, const Tolerance &t) {
mpfr_init2(value, mpfrPrecision);
mpfr_set_zero(value, 1); // Set to positive zero.
MPFRNumber xExponent(fputil::getExponent(x));
MPFRNumber xExponent(fputil::FPBits<XType>(x).getExponent());
// E = 2^E
mpfr_exp2(xExponent.value, xExponent.value, MPFR_RNDN);
uint32_t bitMask = 1 << (t.width - 1);
Expand Down Expand Up @@ -155,24 +164,28 @@ namespace internal {

template <typename T>
void MPFRMatcher<T>::explainError(testutils::StreamWrapper &OS) {
using fputil::valueAsBits;

MPFRNumber mpfrResult(operation, input);
MPFRNumber mpfrInput(input);
MPFRNumber mpfrMatchValue(matchValue);
MPFRNumber mpfrToleranceValue(matchValue, tolerance);
FPBits<T> inputBits(input);
FPBits<T> matchBits(matchValue);
// TODO: Call to llvm::utohexstr implicitly converts __uint128_t values to
// uint64_t values. This can be fixed using a custom wrapper for
// llvm::utohexstr to handle __uint128_t values correctly.
OS << "Match value not within tolerance value of MPFR result:\n"
<< " Input decimal: " << mpfrInput.str() << '\n'
<< " Input bits: 0x" << llvm::utohexstr(valueAsBits(input)) << '\n'
<< " Input bits: 0x" << llvm::utohexstr(inputBits.bitsAsUInt()) << '\n'
<< " Match decimal: " << mpfrMatchValue.str() << '\n'
<< " Match bits: 0x" << llvm::utohexstr(valueAsBits(matchValue))
<< '\n'
<< " Match bits: 0x" << llvm::utohexstr(matchBits.bitsAsUInt()) << '\n'
<< " MPFR result: " << mpfrResult.str() << '\n'
<< "Tolerance value: " << mpfrToleranceValue.str() << '\n';
}

template void MPFRMatcher<float>::explainError(testutils::StreamWrapper &);
template void MPFRMatcher<double>::explainError(testutils::StreamWrapper &);
template void
MPFRMatcher<long double>::explainError(testutils::StreamWrapper &);

template <typename T>
bool compare(Operation op, T input, T libcResult, const Tolerance &t) {
Expand All @@ -185,6 +198,8 @@ bool compare(Operation op, T input, T libcResult, const Tolerance &t) {

template bool compare<float>(Operation, float, float, const Tolerance &);
template bool compare<double>(Operation, double, double, const Tolerance &);
template bool compare<long double>(Operation, long double, long double,
const Tolerance &);

} // namespace internal

Expand Down