diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt --- a/libc/config/linux/aarch64/entrypoints.txt +++ b/libc/config/linux/aarch64/entrypoints.txt @@ -22,6 +22,7 @@ libc.src.math.exp2f libc.src.math.fabs libc.src.math.fabsf + libc.src.math.fabsl libc.src.math.floor libc.src.math.floorf libc.src.math.frexp @@ -36,4 +37,5 @@ libc.src.math.sinf libc.src.math.trunc libc.src.math.truncf + libc.src.math.truncl ) diff --git a/libc/config/linux/api.td b/libc/config/linux/api.td --- a/libc/config/linux/api.td +++ b/libc/config/linux/api.td @@ -157,6 +157,7 @@ "cosf", "fabs", "fabsf", + "fabsl", "floor", "floorf", "frexp", @@ -173,6 +174,7 @@ "sinf", "trunc", "truncf", + "truncl", ]; } diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -54,6 +54,7 @@ libc.src.math.exp2f libc.src.math.fabs libc.src.math.fabsf + libc.src.math.fabsl libc.src.math.floor libc.src.math.floorf libc.src.math.frexp @@ -68,4 +69,5 @@ libc.src.math.sinf libc.src.math.trunc libc.src.math.truncf + libc.src.math.truncl ) diff --git a/libc/spec/stdc.td b/libc/spec/stdc.td --- a/libc/spec/stdc.td +++ b/libc/spec/stdc.td @@ -197,6 +197,7 @@ FunctionSpec<"fabs", RetValSpec, [ArgSpec]>, FunctionSpec<"fabsf", RetValSpec, [ArgSpec]>, + FunctionSpec<"fabsl", RetValSpec, [ArgSpec]>, FunctionSpec<"floor", RetValSpec, [ArgSpec]>, FunctionSpec<"floorf", RetValSpec, [ArgSpec]>, @@ -221,6 +222,7 @@ FunctionSpec<"trunc", RetValSpec, [ArgSpec]>, FunctionSpec<"truncf", RetValSpec, [ArgSpec]>, + FunctionSpec<"truncl", RetValSpec, [ArgSpec]>, ] >; diff --git a/libc/src/math/CMakeLists.txt b/libc/src/math/CMakeLists.txt --- a/libc/src/math/CMakeLists.txt +++ b/libc/src/math/CMakeLists.txt @@ -64,6 +64,8 @@ fabs.h DEPENDS libc.utils.FPUtil.fputil + COMPILE_OPTIONS + -O2 ) add_entrypoint_object( @@ -74,6 +76,20 @@ fabsf.h DEPENDS libc.utils.FPUtil.fputil + COMPILE_OPTIONS + -O2 +) + +add_entrypoint_object( + fabsl + SRCS + fabsl.cpp + HDRS + fabsl.h + DEPENDS + libc.utils.FPUtil.fputil + COMPILE_OPTIONS + -O2 ) add_entrypoint_object( @@ -84,6 +100,8 @@ trunc.h DEPENDS libc.utils.FPUtil.fputil + COMPILE_OPTIONS + -O2 ) add_entrypoint_object( @@ -94,6 +112,20 @@ truncf.h DEPENDS libc.utils.FPUtil.fputil + COMPILE_OPTIONS + -O2 +) + +add_entrypoint_object( + truncl + SRCS + truncl.cpp + HDRS + truncl.h + DEPENDS + libc.utils.FPUtil.fputil + COMPILE_OPTIONS + -O2 ) add_entrypoint_object( @@ -104,6 +136,8 @@ ceil.h DEPENDS libc.utils.FPUtil.fputil + COMPILE_OPTIONS + -O2 ) add_entrypoint_object( @@ -114,6 +148,8 @@ ceilf.h DEPENDS libc.utils.FPUtil.fputil + COMPILE_OPTIONS + -O2 ) add_entrypoint_object( @@ -124,6 +160,8 @@ floor.h DEPENDS libc.utils.FPUtil.fputil + COMPILE_OPTIONS + -O2 ) add_entrypoint_object( @@ -134,6 +172,8 @@ floorf.h DEPENDS libc.utils.FPUtil.fputil + COMPILE_OPTIONS + -O2 ) add_entrypoint_object( diff --git a/libc/utils/FPUtil/BasicOperations.h b/libc/src/math/fabsl.h copy from libc/utils/FPUtil/BasicOperations.h copy to libc/src/math/fabsl.h --- a/libc/utils/FPUtil/BasicOperations.h +++ b/libc/src/math/fabsl.h @@ -1,4 +1,4 @@ -//===-- Basic operations on floating point numbers --------------*- C++ -*-===// +//===-- Implementation header for fabsf -------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,16 +6,13 @@ // //===----------------------------------------------------------------------===// -#include "FloatOperations.h" +#ifndef LLVM_LIBC_SRC_MATH_FABSL_H +#define LLVM_LIBC_SRC_MATH_FABSL_H namespace __llvm_libc { -namespace fputil { -template ::Value, int> = 0> -static inline T abs(T x) { - return valueFromBits(absBits(x)); -} +long double fabsl(long double x); -} // namespace fputil } // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_MATH_FABSL_H diff --git a/libc/utils/FPUtil/BasicOperations.h b/libc/src/math/fabsl.cpp copy from libc/utils/FPUtil/BasicOperations.h copy to libc/src/math/fabsl.cpp --- a/libc/utils/FPUtil/BasicOperations.h +++ b/libc/src/math/fabsl.cpp @@ -1,4 +1,4 @@ -//===-- Basic operations on floating point numbers --------------*- C++ -*-===// +//===-- Implementation of fabsf function ----------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,16 +6,13 @@ // //===----------------------------------------------------------------------===// -#include "FloatOperations.h" +#include "src/__support/common.h" +#include "utils/FPUtil/BasicOperations.h" namespace __llvm_libc { -namespace fputil { -template ::Value, int> = 0> -static inline T abs(T x) { - return valueFromBits(absBits(x)); +long double LLVM_LIBC_ENTRYPOINT(fabsl)(long double x) { + return fputil::abs(x); } -} // namespace fputil } // namespace __llvm_libc diff --git a/libc/utils/FPUtil/BasicOperations.h b/libc/src/math/truncl.h copy from libc/utils/FPUtil/BasicOperations.h copy to libc/src/math/truncl.h --- a/libc/utils/FPUtil/BasicOperations.h +++ b/libc/src/math/truncl.h @@ -1,4 +1,4 @@ -//===-- Basic operations on floating point numbers --------------*- C++ -*-===// +//===-- Implementation header for truncl ------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,16 +6,13 @@ // //===----------------------------------------------------------------------===// -#include "FloatOperations.h" +#ifndef LLVM_LIBC_SRC_MATH_TRUNCL_H +#define LLVM_LIBC_SRC_MATH_TRUNCL_H namespace __llvm_libc { -namespace fputil { -template ::Value, int> = 0> -static inline T abs(T x) { - return valueFromBits(absBits(x)); -} +long double truncl(long double x); -} // namespace fputil } // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_MATH_TRUNCL_H diff --git a/libc/utils/FPUtil/BasicOperations.h b/libc/src/math/truncl.cpp copy from libc/utils/FPUtil/BasicOperations.h copy to libc/src/math/truncl.cpp --- a/libc/utils/FPUtil/BasicOperations.h +++ b/libc/src/math/truncl.cpp @@ -1,4 +1,4 @@ -//===-- Basic operations on floating point numbers --------------*- C++ -*-===// +//===-- Implementation of truncl function ---------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,16 +6,13 @@ // //===----------------------------------------------------------------------===// -#include "FloatOperations.h" +#include "src/__support/common.h" +#include "utils/FPUtil/NearestIntegerOperations.h" namespace __llvm_libc { -namespace fputil { -template ::Value, int> = 0> -static inline T abs(T x) { - return valueFromBits(absBits(x)); +long double LLVM_LIBC_ENTRYPOINT(truncl)(long double x) { + return fputil::trunc(x); } -} // namespace fputil } // namespace __llvm_libc diff --git a/libc/test/src/math/CMakeLists.txt b/libc/test/src/math/CMakeLists.txt --- a/libc/test/src/math/CMakeLists.txt +++ b/libc/test/src/math/CMakeLists.txt @@ -97,6 +97,19 @@ libc.utils.FPUtil.fputil ) +add_math_unittest( + fabsl_test + NEED_MPFR + SUITE + libc_math_unittests + SRCS + fabsl_test.cpp + DEPENDS + libc.include.math + libc.src.math.fabsl + libc.utils.FPUtil.fputil +) + add_math_unittest( trunc_test NEED_MPFR @@ -123,6 +136,19 @@ libc.utils.FPUtil.fputil ) +add_math_unittest( + truncl_test + NEED_MPFR + SUITE + libc_math_unittests + SRCS + truncl_test.cpp + DEPENDS + libc.include.math + libc.src.math.truncl + libc.utils.FPUtil.fputil +) + add_math_unittest( ceil_test NEED_MPFR diff --git a/libc/test/src/math/fabsl_test.cpp b/libc/test/src/math/fabsl_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/math/fabsl_test.cpp @@ -0,0 +1,46 @@ +//===-- Unittests for fabsl -----------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "include/math.h" +#include "src/math/fabsl.h" +#include "utils/FPUtil/FPBits.h" +#include "utils/MPFRWrapper/MPFRUtils.h" +#include "utils/UnitTest/Test.h" + +using FPBits = __llvm_libc::fputil::FPBits; + +namespace mpfr = __llvm_libc::testing::mpfr; + +// Zero tolerance; As in, exact match with MPFR result. +static constexpr mpfr::Tolerance tolerance{mpfr::Tolerance::floatPrecision, 0, + 0}; + +TEST(FabslTest, SpecialNumbers) { + EXPECT_TRUE(FPBits::zero() == __llvm_libc::fabsl(FPBits::zero())); + EXPECT_TRUE(FPBits::zero() == __llvm_libc::fabsl(FPBits::negZero())); + + EXPECT_TRUE(FPBits::inf() == __llvm_libc::fabsl(FPBits::inf())); + EXPECT_TRUE(FPBits::inf() == __llvm_libc::fabsl(FPBits::negInf())); + + long double nan = FPBits::buildNaN(1); + ASSERT_TRUE(isnan(nan) != 0); + ASSERT_TRUE(isnan(__llvm_libc::fabsl(nan)) != 0); +} + +TEST(FabslTest, InLongDoubleRange) { + using UIntType = FPBits::UIntType; + constexpr UIntType count = 10000000; + constexpr UIntType step = UIntType(-1) / count; + for (UIntType i = 0, v = 0; i <= count; ++i, v += step) { + long double x = FPBits(v); + if (isnan(x) || isinf(x)) + continue; + ASSERT_MPFR_MATCH(mpfr::Operation::Abs, x, __llvm_libc::fabsl(x), + tolerance); + } +} diff --git a/libc/test/src/math/truncl_test.cpp b/libc/test/src/math/truncl_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/math/truncl_test.cpp @@ -0,0 +1,65 @@ +//===-- Unittests for truncl ----------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "include/math.h" +#include "src/math/truncl.h" +#include "utils/FPUtil/FPBits.h" +#include "utils/MPFRWrapper/MPFRUtils.h" +#include "utils/UnitTest/Test.h" + +using FPBits = __llvm_libc::fputil::FPBits; + +namespace mpfr = __llvm_libc::testing::mpfr; + +// Zero tolerance; As in, exact match with MPFR result. +static constexpr mpfr::Tolerance tolerance{mpfr::Tolerance::floatPrecision, 0, + 0}; + +TEST(TrunclTest, SpecialNumbers) { + ASSERT_TRUE(FPBits::zero() == __llvm_libc::truncl(FPBits::zero())); + ASSERT_TRUE(FPBits::negZero() == __llvm_libc::truncl(FPBits::negZero())); + + ASSERT_TRUE(FPBits::inf() == __llvm_libc::truncl(FPBits::inf())); + ASSERT_TRUE(FPBits::negInf() == __llvm_libc::truncl(FPBits::negInf())); + + long double nan = FPBits::buildNaN(1); + ASSERT_TRUE(isnan(nan) != 0); + ASSERT_TRUE(isnan(__llvm_libc::truncl(nan)) != 0); +} + +TEST(TrunclTest, RoundedNumbers) { + ASSERT_TRUE(FPBits(1.0l) == __llvm_libc::truncl(1.0l)); + ASSERT_TRUE(FPBits(-1.0l) == __llvm_libc::truncl(-1.0l)); + ASSERT_TRUE(FPBits(10.0l) == __llvm_libc::truncl(10.0l)); + ASSERT_TRUE(FPBits(-10.0l) == __llvm_libc::truncl(-10.0l)); + ASSERT_TRUE(FPBits(1234.0l) == __llvm_libc::truncl(1234.0l)); + ASSERT_TRUE(FPBits(-1234.0l) == __llvm_libc::truncl(-1234.0l)); +} + +TEST(TrunclTest, Fractions) { + ASSERT_TRUE(FPBits(1.0l) == __llvm_libc::truncl(1.5l)); + ASSERT_TRUE(FPBits(-1.0l) == __llvm_libc::truncl(-1.75l)); + ASSERT_TRUE(FPBits(10.0l) == __llvm_libc::truncl(10.32l)); + ASSERT_TRUE(FPBits(-10.0l) == __llvm_libc::truncl(-10.65l)); + ASSERT_TRUE(FPBits(1234.0l) == __llvm_libc::truncl(1234.78l)); + ASSERT_TRUE(FPBits(-1234.0l) == __llvm_libc::truncl(-1234.96l)); +} + +TEST(TrunclTest, InLongDoubleRange) { + using UIntType = FPBits::UIntType; + constexpr UIntType count = 10000000; + constexpr UIntType step = UIntType(-1) / count; + for (UIntType i = 0, v = 0; i <= count; ++i, v += step) { + long double x = FPBits(v); + if (isnan(x) || isinf(x)) + continue; + + ASSERT_MPFR_MATCH(mpfr::Operation::Trunc, x, __llvm_libc::truncl(x), + tolerance); + } +} diff --git a/libc/utils/FPUtil/BasicOperations.h b/libc/utils/FPUtil/BasicOperations.h --- a/libc/utils/FPUtil/BasicOperations.h +++ b/libc/utils/FPUtil/BasicOperations.h @@ -6,7 +6,12 @@ // //===----------------------------------------------------------------------===// -#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 { @@ -14,8 +19,12 @@ template ::Value, int> = 0> static inline T abs(T x) { - return valueFromBits(absBits(x)); + FPBits bits(x); + bits.sign = 0; + return T(bits); } } // namespace fputil } // namespace __llvm_libc + +#endif // LLVM_LIBC_UTILS_FPUTIL_BASIC_OPERATIONS_H diff --git a/libc/utils/FPUtil/CMakeLists.txt b/libc/utils/FPUtil/CMakeLists.txt --- a/libc/utils/FPUtil/CMakeLists.txt +++ b/libc/utils/FPUtil/CMakeLists.txt @@ -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 ) diff --git a/libc/utils/FPUtil/FPBits.h b/libc/utils/FPUtil/FPBits.h new file mode 100644 --- /dev/null +++ b/libc/utils/FPUtil/FPBits.h @@ -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 + +namespace __llvm_libc { +namespace fputil { + +template struct MantissaWidth {}; +template <> struct MantissaWidth { + static constexpr unsigned value = 23; +}; +template <> struct MantissaWidth { + static constexpr unsigned value = 52; +}; + +template struct ExponentWidth {}; +template <> struct ExponentWidth { + static constexpr unsigned value = 8; +}; +template <> struct ExponentWidth { + static constexpr unsigned value = 11; +}; +template <> struct ExponentWidth { + static constexpr unsigned value = 15; +}; + +template struct FPUIntType {}; +template <> struct FPUIntType { using Type = uint32_t; }; +template <> struct FPUIntType { 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 { + static constexpr unsigned value = 112; +}; + +template <> struct FPUIntType { 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 struct __attribute__((packed)) FPBits { + static_assert(cpp::IsFloatingPointType::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::Type; + + UIntType mantissa : MantissaWidth::value; + uint16_t exponent : ExponentWidth::value; + uint8_t sign : 1; + + static constexpr int exponentBias = (1 << (ExponentWidth::value - 1)) - 1; + static constexpr int maxExponent = (1 << ExponentWidth::value) - 1; + + // We don't want accidental type promotions/conversions so we require exact + // type match. + template ::Value, int> = 0> + explicit FPBits(XType x) { + *this = *reinterpret_cast *>(&x); + } + + operator T() { return *reinterpret_cast(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 ::Value, int> = 0> + explicit FPBits(XType x) { + // The last 4 bytes of v are ignored in case of i386. + *this = *reinterpret_cast *>(&x); + } + + UIntType bitsAsUInt() const { + return *reinterpret_cast(this); + } + + static FPBits zero() { return FPBits(T(0.0)); } + + static FPBits negZero() { + FPBits bits(T(0.0)); + bits.sign = 1; + return bits; + } + + static FPBits inf() { + FPBits bits(T(0.0)); + bits.exponent = maxExponent; + return bits; + } + + static FPBits negInf() { + FPBits bits(T(0.0)); + bits.exponent = maxExponent; + bits.sign = 1; + return bits; + } + + static T buildNaN(UIntType v) { + FPBits 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 diff --git a/libc/utils/FPUtil/FloatOperations.h b/libc/utils/FPUtil/FloatOperations.h --- a/libc/utils/FPUtil/FloatOperations.h +++ b/libc/utils/FPUtil/FloatOperations.h @@ -57,7 +57,6 @@ return getExponentFromBits(valueAsBits(x)); } - } // namespace fputil } // namespace __llvm_libc diff --git a/libc/utils/FPUtil/LongDoubleBitsX86.h b/libc/utils/FPUtil/LongDoubleBitsX86.h new file mode 100644 --- /dev/null +++ b/libc/utils/FPUtil/LongDoubleBitsX86.h @@ -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 + +namespace __llvm_libc { +namespace fputil { + +template <> struct MantissaWidth { + static constexpr unsigned value = 63; +}; + +template 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 { + using UIntType = __uint128_t; + + static constexpr int exponentBias = 0x3FFF; + static constexpr int maxExponent = 0x7FFF; + + UIntType mantissa : MantissaWidth::value; + uint8_t implicitBit : 1; + uint16_t exponent : ExponentWidth::value; + uint8_t sign : 1; + uint64_t padding : Padding::Value; + + template ::Value, int> = 0> + explicit FPBits(XType x) { + *this = *reinterpret_cast *>(&x); + } + + operator long double() { return *reinterpret_cast(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 ::Value, int> = 0> + explicit FPBits(XType x) { + // The last 4 bytes of v are ignored in case of i386. + *this = *reinterpret_cast *>(&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 *>(&result) = *this; + return result; + } + + static FPBits zero() { return FPBits(0.0l); } + + static FPBits negZero() { + FPBits bits(0.0l); + bits.sign = 1; + return bits; + } + + static FPBits inf() { + FPBits bits(0.0l); + bits.exponent = maxExponent; + bits.implicitBit = 1; + return bits; + } + + static FPBits negInf() { + FPBits bits(0.0l); + bits.exponent = maxExponent; + bits.implicitBit = 1; + bits.sign = 1; + return bits; + } + + static long double buildNaN(UIntType v) { + FPBits bits(0.0l); + bits.exponent = maxExponent; + bits.implicitBit = 1; + bits.mantissa = v; + return bits; + } +}; + +static_assert( + sizeof(FPBits) == 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 diff --git a/libc/utils/FPUtil/NearestIntegerOperations.h b/libc/utils/FPUtil/NearestIntegerOperations.h --- a/libc/utils/FPUtil/NearestIntegerOperations.h +++ b/libc/utils/FPUtil/NearestIntegerOperations.h @@ -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" @@ -21,32 +22,33 @@ template ::Value, int> = 0> static inline T trunc(T x) { - using Properties = FloatProperties; - using BitsType = typename FloatProperties::BitsType; + FPBits 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(Properties::mantissaWidth)) + if (exponent >= static_cast(MantissaWidth::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::value - exponent; + bits.mantissa = (bits.mantissa >> trimSize) << trimSize; + return bits; } template #include +template using FPBits = __llvm_libc::fputil::FPBits; + 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; @@ -48,6 +50,13 @@ mpfr_set_d(value, x, MPFR_RNDN); } + template ::Value, int> = 0> + explicit MPFRNumber(XType x) { + mpfr_init2(value, mpfrPrecision); + mpfr_set_ld(value, x, MPFR_RNDN); + } + template ::Value, int> = 0> explicit MPFRNumber(XType x) { @@ -58,7 +67,7 @@ template 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(x).getExponent()); // E = 2^E mpfr_exp2(xExponent.value, xExponent.value, MPFR_RNDN); uint32_t bitMask = 1 << (t.width - 1); @@ -155,24 +164,28 @@ template void MPFRMatcher::explainError(testutils::StreamWrapper &OS) { - using fputil::valueAsBits; - MPFRNumber mpfrResult(operation, input); MPFRNumber mpfrInput(input); MPFRNumber mpfrMatchValue(matchValue); MPFRNumber mpfrToleranceValue(matchValue, tolerance); + FPBits inputBits(input); + FPBits 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::explainError(testutils::StreamWrapper &); template void MPFRMatcher::explainError(testutils::StreamWrapper &); +template void +MPFRMatcher::explainError(testutils::StreamWrapper &); template bool compare(Operation op, T input, T libcResult, const Tolerance &t) { @@ -185,6 +198,8 @@ template bool compare(Operation, float, float, const Tolerance &); template bool compare(Operation, double, double, const Tolerance &); +template bool compare(Operation, long double, long double, + const Tolerance &); } // namespace internal