From 32a22a423c71c8dc479cdbd5d6de98772431b10d Mon Sep 17 00:00:00 2001 From: Siva Chandra Reddy Date: Thu, 7 May 2020 23:19:09 -0700 Subject: [PATCH] [libc] Consolidate floating point utils into a single utils library. A new utils library named 'fputil' is added. This library is used in math tests and the MPFR wrapper. The math implementations will be modified to use this library in a later round. Reviewers: phosek Differential Revision: https://reviews.llvm.org/D79724 --- libc/test/src/math/CMakeLists.txt | 15 ++-- libc/test/src/math/cosf_test.cpp | 58 ++++++++------- libc/test/src/math/float.h | 49 ------------- libc/test/src/math/sdcomp26094.h | 11 ++- libc/test/src/math/sincosf_test.cpp | 82 +++++++++++---------- libc/test/src/math/sinf_test.cpp | 60 ++++++++------- libc/utils/CMakeLists.txt | 1 + libc/utils/FPUtil/BitPatterns.h | 62 ++++++++++++++++ libc/utils/FPUtil/CMakeLists.txt | 9 +++ libc/utils/FPUtil/FloatOperations.h | 102 ++++++++++++++++++++++++++ libc/utils/FPUtil/FloatProperties.h | 72 ++++++++++++++++++ libc/utils/MPFRWrapper/CMakeLists.txt | 2 +- libc/utils/MPFRWrapper/MPFRUtils.cpp | 49 ++----------- 13 files changed, 376 insertions(+), 196 deletions(-) delete mode 100644 libc/test/src/math/float.h create mode 100644 libc/utils/FPUtil/BitPatterns.h create mode 100644 libc/utils/FPUtil/CMakeLists.txt create mode 100644 libc/utils/FPUtil/FloatOperations.h create mode 100644 libc/utils/FPUtil/FloatProperties.h diff --git a/libc/test/src/math/CMakeLists.txt b/libc/test/src/math/CMakeLists.txt index 8f701f6b4b74..3790a476369e 100644 --- a/libc/test/src/math/CMakeLists.txt +++ b/libc/test/src/math/CMakeLists.txt @@ -23,12 +23,6 @@ function(add_math_unittest name) endif() endfunction(add_math_unittest) -add_header_library( - float_utils - HDRS - float.h -) - add_math_unittest( cosf_test NEED_MPFR @@ -39,9 +33,10 @@ add_math_unittest( HDRS sdcomp26094.h DEPENDS - .float_utils + libc.include.errno libc.src.math.cosf libc.utils.CPP.standalone_cpp + libc.utils.FPUtil.fputil ) add_math_unittest( @@ -54,9 +49,10 @@ add_math_unittest( HDRS sdcomp26094.h DEPENDS - .float_utils + libc.include.errno libc.src.math.sinf libc.utils.CPP.standalone_cpp + libc.utils.FPUtil.fputil ) add_math_unittest( @@ -69,7 +65,8 @@ add_math_unittest( HDRS sdcomp26094.h DEPENDS - .float_utils + libc.include.errno libc.src.math.sincosf libc.utils.CPP.standalone_cpp + libc.utils.FPUtil.fputil ) diff --git a/libc/test/src/math/cosf_test.cpp b/libc/test/src/math/cosf_test.cpp index 54bba168cadf..f9fc9c2e2d0d 100644 --- a/libc/test/src/math/cosf_test.cpp +++ b/libc/test/src/math/cosf_test.cpp @@ -6,22 +6,27 @@ // //===----------------------------------------------------------------------===// +#include "include/errno.h" #include "include/math.h" #include "src/errno/llvmlibc_errno.h" #include "src/math/cosf.h" -#include "src/math/math_utils.h" -#include "test/src/math/float.h" #include "test/src/math/sdcomp26094.h" #include "utils/CPP/Array.h" +#include "utils/FPUtil/BitPatterns.h" +#include "utils/FPUtil/FloatOperations.h" +#include "utils/FPUtil/FloatProperties.h" #include "utils/MPFRWrapper/MPFRUtils.h" #include "utils/UnitTest/Test.h" #include -using __llvm_libc::as_float; -using __llvm_libc::as_uint32_bits; +using __llvm_libc::fputil::isNegativeQuietNaN; +using __llvm_libc::fputil::isQuietNaN; +using __llvm_libc::fputil::valueAsBits; +using __llvm_libc::fputil::valueFromBits; + +using BitPatterns = __llvm_libc::fputil::BitPatterns; -using __llvm_libc::testing::FloatBits; using __llvm_libc::testing::sdcomp26094Values; namespace mpfr = __llvm_libc::testing::mpfr; @@ -34,38 +39,37 @@ static constexpr mpfr::Tolerance tolerance{mpfr::Tolerance::floatPrecision, 12, TEST(CosfTest, SpecialNumbers) { llvmlibc_errno = 0; - EXPECT_TRUE(FloatBits::isQNan( - as_uint32_bits(__llvm_libc::cosf(as_float(FloatBits::QNan))))); + EXPECT_TRUE( + isQuietNaN(__llvm_libc::cosf(valueFromBits(BitPatterns::aQuietNaN)))); EXPECT_EQ(llvmlibc_errno, 0); - EXPECT_TRUE(FloatBits::isNegQNan( - as_uint32_bits(__llvm_libc::cosf(as_float(FloatBits::NegQNan))))); + EXPECT_TRUE(isNegativeQuietNaN( + __llvm_libc::cosf(valueFromBits(BitPatterns::aNegativeQuietNaN)))); EXPECT_EQ(llvmlibc_errno, 0); - EXPECT_TRUE(FloatBits::isQNan( - as_uint32_bits(__llvm_libc::cosf(as_float(FloatBits::SNan))))); + EXPECT_TRUE(isQuietNaN( + __llvm_libc::cosf(valueFromBits(BitPatterns::aSignallingNaN)))); EXPECT_EQ(llvmlibc_errno, 0); - EXPECT_TRUE(FloatBits::isNegQNan( - as_uint32_bits(__llvm_libc::cosf(as_float(FloatBits::NegSNan))))); + EXPECT_TRUE(isNegativeQuietNaN( + __llvm_libc::cosf(valueFromBits(BitPatterns::aNegativeSignallingNaN)))); EXPECT_EQ(llvmlibc_errno, 0); - EXPECT_EQ(FloatBits::One, - as_uint32_bits(__llvm_libc::cosf(as_float(FloatBits::Zero)))); + EXPECT_EQ(BitPatterns::one, + valueAsBits(__llvm_libc::cosf(valueFromBits(BitPatterns::zero)))); EXPECT_EQ(llvmlibc_errno, 0); - EXPECT_EQ(FloatBits::One, - as_uint32_bits(__llvm_libc::cosf(as_float(FloatBits::NegZero)))); + EXPECT_EQ(BitPatterns::one, valueAsBits(__llvm_libc::cosf( + valueFromBits(BitPatterns::negZero)))); EXPECT_EQ(llvmlibc_errno, 0); llvmlibc_errno = 0; - EXPECT_TRUE(FloatBits::isQNan( - as_uint32_bits(__llvm_libc::cosf(as_float(FloatBits::Inf))))); + EXPECT_TRUE(isQuietNaN(__llvm_libc::cosf(valueFromBits(BitPatterns::inf)))); EXPECT_EQ(llvmlibc_errno, EDOM); llvmlibc_errno = 0; - EXPECT_TRUE(FloatBits::isNegQNan( - as_uint32_bits(__llvm_libc::cosf(as_float(FloatBits::NegInf))))); + EXPECT_TRUE(isNegativeQuietNaN( + __llvm_libc::cosf(valueFromBits(BitPatterns::negInf)))); EXPECT_EQ(llvmlibc_errno, EDOM); } @@ -73,7 +77,7 @@ TEST(CosfTest, InFloatRange) { constexpr uint32_t count = 1000000; constexpr uint32_t step = UINT32_MAX / count; for (uint32_t i = 0, v = 0; i <= count; ++i, v += step) { - float x = as_float(v); + float x = valueFromBits(v); if (isnan(x) || isinf(x)) continue; ASSERT_MPFR_MATCH(mpfr::OP_Cos, x, __llvm_libc::cosf(x), tolerance); @@ -82,22 +86,22 @@ TEST(CosfTest, InFloatRange) { // For small values, cos(x) is 1. TEST(CosfTest, SmallValues) { - float x = as_float(0x17800000); + float x = valueFromBits(0x17800000U); float result = __llvm_libc::cosf(x); EXPECT_MPFR_MATCH(mpfr::OP_Cos, x, result, tolerance); - EXPECT_EQ(FloatBits::One, as_uint32_bits(result)); + EXPECT_EQ(BitPatterns::one, valueAsBits(result)); - x = as_float(0x0040000); + x = valueFromBits(0x0040000U); result = __llvm_libc::cosf(x); EXPECT_MPFR_MATCH(mpfr::OP_Cos, x, result, tolerance); - EXPECT_EQ(FloatBits::One, as_uint32_bits(result)); + EXPECT_EQ(BitPatterns::one, valueAsBits(result)); } // SDCOMP-26094: check cosf in the cases for which the range reducer // returns values furthest beyond its nominal upper bound of pi/4. TEST(CosfTest, SDCOMP_26094) { for (uint32_t v : sdcomp26094Values) { - float x = as_float(v); + float x = valueFromBits(v); ASSERT_MPFR_MATCH(mpfr::OP_Cos, x, __llvm_libc::cosf(x), tolerance); } } diff --git a/libc/test/src/math/float.h b/libc/test/src/math/float.h deleted file mode 100644 index bfa15a18ce3f..000000000000 --- a/libc/test/src/math/float.h +++ /dev/null @@ -1,49 +0,0 @@ -//===-- Single precision floating point test utils --------------*- 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_TEST_SRC_MATH_FLOAT_H -#define LLVM_LIBC_TEST_SRC_MATH_FLOAT_H - -#include "src/math/math_utils.h" - -namespace __llvm_libc { -namespace testing { - -struct FloatBits { - // The various NaN bit patterns here are just one of the many possible - // patterns. The functions isQNan and isNegQNan can help understand why. - - static const uint32_t QNan = 0x7fc00000; - static const uint32_t NegQNan = 0xffc00000; - - static const uint32_t SNan = 0x7f800001; - static const uint32_t NegSNan = 0xff800001; - - static bool isQNan(float f) { - uint32_t bits = as_uint32_bits(f); - return ((0x7fc00000 & bits) != 0) && ((0x80000000 & bits) == 0); - } - - static bool isNegQNan(float f) { - uint32_t bits = as_uint32_bits(f); - return 0xffc00000 & bits; - } - - static constexpr uint32_t Zero = 0x0; - static constexpr uint32_t NegZero = 0x80000000; - - static constexpr uint32_t Inf = 0x7f800000; - static constexpr uint32_t NegInf = 0xff800000; - - static constexpr uint32_t One = 0x3f800000; -}; - -} // namespace testing -} // namespace __llvm_libc - -#endif // LLVM_LIBC_TEST_SRC_MATH_FLOAT_H diff --git a/libc/test/src/math/sdcomp26094.h b/libc/test/src/math/sdcomp26094.h index 7ebcd60e4af2..7e477534c98b 100644 --- a/libc/test/src/math/sdcomp26094.h +++ b/libc/test/src/math/sdcomp26094.h @@ -10,14 +10,17 @@ #define LLVM_LIBC_TEST_SRC_MATH_SDCOMP26094_H #include "utils/CPP/Array.h" +#include "utils/FPUtil/BitPatterns.h" namespace __llvm_libc { namespace testing { -static constexpr __llvm_libc::cpp::Array sdcomp26094Values{ - 0x46427f1b, 0x4647e568, 0x46428bac, 0x4647f1f9, 0x4647fe8a, - 0x45d8d7f1, 0x45d371a4, 0x45ce0b57, 0x45d35882, 0x45cdf235, -}; +static constexpr __llvm_libc::cpp::Array::BitsType, + 10> + sdcomp26094Values{ + 0x46427f1b, 0x4647e568, 0x46428bac, 0x4647f1f9, 0x4647fe8a, + 0x45d8d7f1, 0x45d371a4, 0x45ce0b57, 0x45d35882, 0x45cdf235, + }; } // namespace testing } // namespace __llvm_libc diff --git a/libc/test/src/math/sincosf_test.cpp b/libc/test/src/math/sincosf_test.cpp index 93b827a2ad37..9a87e2c9e58c 100644 --- a/libc/test/src/math/sincosf_test.cpp +++ b/libc/test/src/math/sincosf_test.cpp @@ -6,25 +6,31 @@ // //===----------------------------------------------------------------------===// +#include "include/errno.h" #include "include/math.h" #include "src/errno/llvmlibc_errno.h" -#include "src/math/math_utils.h" #include "src/math/sincosf.h" -#include "test/src/math/float.h" #include "test/src/math/sdcomp26094.h" #include "utils/CPP/Array.h" +#include "utils/FPUtil/BitPatterns.h" +#include "utils/FPUtil/FloatOperations.h" +#include "utils/FPUtil/FloatProperties.h" #include "utils/MPFRWrapper/MPFRUtils.h" #include "utils/UnitTest/Test.h" #include -using __llvm_libc::as_float; -using __llvm_libc::as_uint32_bits; +using __llvm_libc::fputil::isNegativeQuietNaN; +using __llvm_libc::fputil::isQuietNaN; +using __llvm_libc::fputil::valueAsBits; +using __llvm_libc::fputil::valueFromBits; + +using BitPatterns = __llvm_libc::fputil::BitPatterns; -using __llvm_libc::testing::FloatBits; using __llvm_libc::testing::sdcomp26094Values; namespace mpfr = __llvm_libc::testing::mpfr; + // 12 additional bits of precision over the base precision of a |float| // value. static constexpr mpfr::Tolerance tolerance{mpfr::Tolerance::floatPrecision, 12, @@ -34,46 +40,48 @@ TEST(SinCosfTest, SpecialNumbers) { llvmlibc_errno = 0; float sin, cos; - __llvm_libc::sincosf(as_float(FloatBits::QNan), &sin, &cos); - EXPECT_TRUE(FloatBits::isQNan(as_uint32_bits(cos))); - EXPECT_TRUE(FloatBits::isQNan(as_uint32_bits(sin))); + __llvm_libc::sincosf(valueFromBits(BitPatterns::aQuietNaN), &sin, &cos); + EXPECT_TRUE(isQuietNaN(cos)); + EXPECT_TRUE(isQuietNaN(sin)); EXPECT_EQ(llvmlibc_errno, 0); - __llvm_libc::sincosf(as_float(FloatBits::NegQNan), &sin, &cos); - EXPECT_TRUE(FloatBits::isNegQNan(as_uint32_bits(cos))); - EXPECT_TRUE(FloatBits::isNegQNan(as_uint32_bits(sin))); + __llvm_libc::sincosf(valueFromBits(BitPatterns::aNegativeQuietNaN), &sin, + &cos); + EXPECT_TRUE(isNegativeQuietNaN(cos)); + EXPECT_TRUE(isNegativeQuietNaN(sin)); EXPECT_EQ(llvmlibc_errno, 0); - __llvm_libc::sincosf(as_float(FloatBits::SNan), &sin, &cos); - EXPECT_TRUE(FloatBits::isQNan(as_uint32_bits(cos))); - EXPECT_TRUE(FloatBits::isQNan(as_uint32_bits(sin))); + __llvm_libc::sincosf(valueFromBits(BitPatterns::aSignallingNaN), &sin, &cos); + EXPECT_TRUE(isQuietNaN(cos)); + EXPECT_TRUE(isQuietNaN(sin)); EXPECT_EQ(llvmlibc_errno, 0); - __llvm_libc::sincosf(as_float(FloatBits::NegSNan), &sin, &cos); - EXPECT_TRUE(FloatBits::isNegQNan(as_uint32_bits(cos))); - EXPECT_TRUE(FloatBits::isNegQNan(as_uint32_bits(sin))); + __llvm_libc::sincosf(valueFromBits(BitPatterns::aNegativeSignallingNaN), &sin, + &cos); + EXPECT_TRUE(isNegativeQuietNaN(cos)); + EXPECT_TRUE(isNegativeQuietNaN(sin)); EXPECT_EQ(llvmlibc_errno, 0); - __llvm_libc::sincosf(as_float(FloatBits::Zero), &sin, &cos); - EXPECT_EQ(FloatBits::One, as_uint32_bits(cos)); - EXPECT_EQ(FloatBits::Zero, as_uint32_bits(sin)); + __llvm_libc::sincosf(valueFromBits(BitPatterns::zero), &sin, &cos); + EXPECT_EQ(BitPatterns::one, valueAsBits(cos)); + EXPECT_EQ(BitPatterns::zero, valueAsBits(sin)); EXPECT_EQ(llvmlibc_errno, 0); - __llvm_libc::sincosf(as_float(FloatBits::NegZero), &sin, &cos); - EXPECT_EQ(FloatBits::One, as_uint32_bits(cos)); - EXPECT_EQ(FloatBits::NegZero, as_uint32_bits(sin)); + __llvm_libc::sincosf(valueFromBits(BitPatterns::negZero), &sin, &cos); + EXPECT_EQ(BitPatterns::one, valueAsBits(cos)); + EXPECT_EQ(BitPatterns::negZero, valueAsBits(sin)); EXPECT_EQ(llvmlibc_errno, 0); llvmlibc_errno = 0; - __llvm_libc::sincosf(as_float(FloatBits::Inf), &sin, &cos); - EXPECT_TRUE(FloatBits::isQNan(as_uint32_bits(cos))); - EXPECT_TRUE(FloatBits::isQNan(as_uint32_bits(sin))); + __llvm_libc::sincosf(valueFromBits(BitPatterns::inf), &sin, &cos); + EXPECT_TRUE(isQuietNaN(cos)); + EXPECT_TRUE(isQuietNaN(sin)); EXPECT_EQ(llvmlibc_errno, EDOM); llvmlibc_errno = 0; - __llvm_libc::sincosf(as_float(FloatBits::NegInf), &sin, &cos); - EXPECT_TRUE(FloatBits::isQNan(as_uint32_bits(cos))); - EXPECT_TRUE(FloatBits::isQNan(as_uint32_bits(sin))); + __llvm_libc::sincosf(valueFromBits(BitPatterns::negInf), &sin, &cos); + EXPECT_TRUE(isQuietNaN(cos)); + EXPECT_TRUE(isQuietNaN(sin)); EXPECT_EQ(llvmlibc_errno, EDOM); } @@ -81,7 +89,7 @@ TEST(SinCosfTest, InFloatRange) { constexpr uint32_t count = 1000000; constexpr uint32_t step = UINT32_MAX / count; for (uint32_t i = 0, v = 0; i <= count; ++i, v += step) { - float x = as_float(v); + float x = valueFromBits(v); if (isnan(x) || isinf(x)) continue; @@ -95,28 +103,28 @@ TEST(SinCosfTest, InFloatRange) { // For small values, cos(x) is 1 and sin(x) is x. TEST(SinCosfTest, SmallValues) { uint32_t bits = 0x17800000; - float x = as_float(bits); + float x = valueFromBits(bits); float result_cos, result_sin; __llvm_libc::sincosf(x, &result_sin, &result_cos); EXPECT_MPFR_MATCH(mpfr::OP_Cos, x, result_cos, tolerance); EXPECT_MPFR_MATCH(mpfr::OP_Sin, x, result_sin, tolerance); - EXPECT_EQ(FloatBits::One, as_uint32_bits(result_cos)); - EXPECT_EQ(bits, as_uint32_bits(result_sin)); + EXPECT_EQ(BitPatterns::one, valueAsBits(result_cos)); + EXPECT_EQ(bits, valueAsBits(result_sin)); bits = 0x00400000; - x = as_float(bits); + x = valueFromBits(bits); __llvm_libc::sincosf(x, &result_sin, &result_cos); EXPECT_MPFR_MATCH(mpfr::OP_Cos, x, result_cos, tolerance); EXPECT_MPFR_MATCH(mpfr::OP_Sin, x, result_sin, tolerance); - EXPECT_EQ(FloatBits::One, as_uint32_bits(result_cos)); - EXPECT_EQ(bits, as_uint32_bits(result_sin)); + EXPECT_EQ(BitPatterns::one, valueAsBits(result_cos)); + EXPECT_EQ(bits, valueAsBits(result_sin)); } // SDCOMP-26094: check sinf in the cases for which the range reducer // returns values furthest beyond its nominal upper bound of pi/4. TEST(SinCosfTest, SDCOMP_26094) { for (uint32_t v : sdcomp26094Values) { - float x = as_float(v); + float x = valueFromBits(v); float sin, cos; __llvm_libc::sincosf(x, &sin, &cos); EXPECT_MPFR_MATCH(mpfr::OP_Cos, x, cos, tolerance); diff --git a/libc/test/src/math/sinf_test.cpp b/libc/test/src/math/sinf_test.cpp index c0ce0755964c..e0821c621dcc 100644 --- a/libc/test/src/math/sinf_test.cpp +++ b/libc/test/src/math/sinf_test.cpp @@ -6,22 +6,27 @@ // //===----------------------------------------------------------------------===// +#include "include/errno.h" #include "include/math.h" #include "src/errno/llvmlibc_errno.h" -#include "src/math/math_utils.h" #include "src/math/sinf.h" -#include "test/src/math/float.h" #include "test/src/math/sdcomp26094.h" #include "utils/CPP/Array.h" +#include "utils/FPUtil/BitPatterns.h" +#include "utils/FPUtil/FloatOperations.h" +#include "utils/FPUtil/FloatProperties.h" #include "utils/MPFRWrapper/MPFRUtils.h" #include "utils/UnitTest/Test.h" #include -using __llvm_libc::as_float; -using __llvm_libc::as_uint32_bits; +using __llvm_libc::fputil::isNegativeQuietNaN; +using __llvm_libc::fputil::isQuietNaN; +using __llvm_libc::fputil::valueAsBits; +using __llvm_libc::fputil::valueFromBits; + +using BitPatterns = __llvm_libc::fputil::BitPatterns; -using __llvm_libc::testing::FloatBits; using __llvm_libc::testing::sdcomp26094Values; namespace mpfr = __llvm_libc::testing::mpfr; @@ -34,38 +39,37 @@ static constexpr mpfr::Tolerance tolerance{mpfr::Tolerance::floatPrecision, 12, TEST(SinfTest, SpecialNumbers) { llvmlibc_errno = 0; - EXPECT_TRUE(FloatBits::isQNan( - as_uint32_bits(__llvm_libc::sinf(as_float(FloatBits::QNan))))); + EXPECT_TRUE( + isQuietNaN(__llvm_libc::sinf(valueFromBits(BitPatterns::aQuietNaN)))); EXPECT_EQ(llvmlibc_errno, 0); - EXPECT_TRUE(FloatBits::isNegQNan( - as_uint32_bits(__llvm_libc::sinf(as_float(FloatBits::NegQNan))))); + EXPECT_TRUE(isNegativeQuietNaN( + __llvm_libc::sinf(valueFromBits(BitPatterns::aNegativeQuietNaN)))); EXPECT_EQ(llvmlibc_errno, 0); - EXPECT_TRUE(FloatBits::isQNan( - as_uint32_bits(__llvm_libc::sinf(as_float(FloatBits::SNan))))); + EXPECT_TRUE(isQuietNaN( + __llvm_libc::sinf(valueFromBits(BitPatterns::aSignallingNaN)))); EXPECT_EQ(llvmlibc_errno, 0); - EXPECT_TRUE(FloatBits::isNegQNan( - as_uint32_bits(__llvm_libc::sinf(as_float(FloatBits::NegSNan))))); + EXPECT_TRUE(isNegativeQuietNaN( + __llvm_libc::sinf(valueFromBits(BitPatterns::aNegativeSignallingNaN)))); EXPECT_EQ(llvmlibc_errno, 0); - EXPECT_EQ(FloatBits::Zero, - as_uint32_bits(__llvm_libc::sinf(as_float(FloatBits::Zero)))); + EXPECT_EQ(BitPatterns::zero, + valueAsBits(__llvm_libc::sinf(valueFromBits(BitPatterns::zero)))); EXPECT_EQ(llvmlibc_errno, 0); - EXPECT_EQ(FloatBits::NegZero, - as_uint32_bits(__llvm_libc::sinf(as_float(FloatBits::NegZero)))); + EXPECT_EQ(BitPatterns::negZero, valueAsBits(__llvm_libc::sinf( + valueFromBits(BitPatterns::negZero)))); EXPECT_EQ(llvmlibc_errno, 0); llvmlibc_errno = 0; - EXPECT_TRUE(FloatBits::isQNan( - as_uint32_bits(__llvm_libc::sinf(as_float(FloatBits::Inf))))); + EXPECT_TRUE(isQuietNaN(__llvm_libc::sinf(valueFromBits(BitPatterns::inf)))); EXPECT_EQ(llvmlibc_errno, EDOM); llvmlibc_errno = 0; - EXPECT_TRUE(FloatBits::isNegQNan( - as_uint32_bits(__llvm_libc::sinf(as_float(FloatBits::NegInf))))); + EXPECT_TRUE(isNegativeQuietNaN( + __llvm_libc::sinf(valueFromBits(BitPatterns::negInf)))); EXPECT_EQ(llvmlibc_errno, EDOM); } @@ -73,7 +77,7 @@ TEST(SinfTest, InFloatRange) { constexpr uint32_t count = 1000000; constexpr uint32_t step = UINT32_MAX / count; for (uint32_t i = 0, v = 0; i <= count; ++i, v += step) { - float x = as_float(v); + float x = valueFromBits(v); if (isnan(x) || isinf(x)) continue; ASSERT_MPFR_MATCH(mpfr::OP_Sin, x, __llvm_libc::sinf(x), tolerance); @@ -81,30 +85,30 @@ TEST(SinfTest, InFloatRange) { } TEST(SinfTest, SpecificBitPatterns) { - float x = as_float(0xc70d39a1); + float x = valueFromBits(0xc70d39a1); EXPECT_MPFR_MATCH(mpfr::OP_Sin, x, __llvm_libc::sinf(x), tolerance); } // For small values, sin(x) is x. TEST(SinfTest, SmallValues) { uint32_t bits = 0x17800000; - float x = as_float(bits); + float x = valueFromBits(bits); float result = __llvm_libc::sinf(x); EXPECT_MPFR_MATCH(mpfr::OP_Sin, x, result, tolerance); - EXPECT_EQ(bits, as_uint32_bits(result)); + EXPECT_EQ(bits, valueAsBits(result)); bits = 0x00400000; - x = as_float(bits); + x = valueFromBits(bits); result = __llvm_libc::sinf(x); EXPECT_MPFR_MATCH(mpfr::OP_Sin, x, result, tolerance); - EXPECT_EQ(bits, as_uint32_bits(result)); + EXPECT_EQ(bits, valueAsBits(result)); } // SDCOMP-26094: check sinf in the cases for which the range reducer // returns values furthest beyond its nominal upper bound of pi/4. TEST(SinfTest, SDCOMP_26094) { for (uint32_t v : sdcomp26094Values) { - float x = as_float(v); + float x = valueFromBits(v); EXPECT_MPFR_MATCH(mpfr::OP_Sin, x, __llvm_libc::sinf(x), tolerance); } } diff --git a/libc/utils/CMakeLists.txt b/libc/utils/CMakeLists.txt index 1e85d05507cc..8a7ee53334a4 100644 --- a/libc/utils/CMakeLists.txt +++ b/libc/utils/CMakeLists.txt @@ -1,4 +1,5 @@ add_subdirectory(CPP) +add_subdirectory(FPUtil) add_subdirectory(HdrGen) add_subdirectory(MPFRWrapper) add_subdirectory(testutils) diff --git a/libc/utils/FPUtil/BitPatterns.h b/libc/utils/FPUtil/BitPatterns.h new file mode 100644 index 000000000000..35c58a43d906 --- /dev/null +++ b/libc/utils/FPUtil/BitPatterns.h @@ -0,0 +1,62 @@ +//===-- Bit patterns of common floating point 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_BIT_PATTERNS_H +#define LLVM_LIBC_UTILS_FPUTIL_BIT_PATTERNS_H + +#include "FloatProperties.h" + +namespace __llvm_libc { +namespace fputil { + +template struct BitPatterns {}; + +template <> struct BitPatterns { + using BitsType = FloatProperties::BitsType; + + static constexpr BitsType inf = 0x7f800000U; + static constexpr BitsType negInf = 0xff800000U; + + static constexpr BitsType zero = 0x0; + static constexpr BitsType negZero = 0x80000000U; + + static constexpr BitsType one = 0x3f800000U; + + // Examples of quiet NAN. + static constexpr BitsType aQuietNaN = 0x7fc00000U; + static constexpr BitsType aNegativeQuietNaN = 0xffc00000U; + + // Examples of signalling NAN. + static constexpr BitsType aSignallingNaN = 0x7f800001U; + static constexpr BitsType aNegativeSignallingNaN = 0xff800001U; +}; + +template <> struct BitPatterns { + using BitsType = FloatProperties::BitsType; + + static constexpr BitsType inf = 0x7ff0000000000000ULL; + static constexpr BitsType negInf = 0xfff0000000000000ULL; + + static constexpr BitsType zero = 0x0ULL; + static constexpr BitsType negZero = 0x8000000000000000ULL; + + static constexpr BitsType one = 0x3FF0000000000000ULL; + + // Examples of quiet NAN. + static constexpr BitsType aQuietNaN = 0x7ff8000000000000ULL; + static constexpr BitsType aNegativeQuietNaN = 0xfff8000000000000ULL; + + // Examples of signalling NAN. + static constexpr BitsType aSignallingNaN = 0x7ff0000000000001ULL; + static constexpr BitsType aNegativeSignallingNaN = 0xfff0000000000001ULL; +}; + +} // namespace fputil +} // namespace __llvm_libc + +#endif // LLVM_LIBC_UTILS_FPUTIL_BIT_PATTERNS_H diff --git a/libc/utils/FPUtil/CMakeLists.txt b/libc/utils/FPUtil/CMakeLists.txt new file mode 100644 index 000000000000..b50ede179fcf --- /dev/null +++ b/libc/utils/FPUtil/CMakeLists.txt @@ -0,0 +1,9 @@ +add_header_library( + fputil + HDRS + BitPatterns.h + FloatOperations.h + FloatProperties.h + DEPS + libc.utils.CPP.standalone_cpp +) diff --git a/libc/utils/FPUtil/FloatOperations.h b/libc/utils/FPUtil/FloatOperations.h new file mode 100644 index 000000000000..902929eb8d47 --- /dev/null +++ b/libc/utils/FPUtil/FloatOperations.h @@ -0,0 +1,102 @@ +//===-- Common operations on floating point 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_FLOAT_OPERATIONS_H +#define LLVM_LIBC_UTILS_FPUTIL_FLOAT_OPERATIONS_H + +#include "BitPatterns.h" +#include "FloatProperties.h" + +#include "utils/CPP/TypeTraits.h" + +namespace __llvm_libc { +namespace fputil { + +// Return the bits of a float value. +template ::Value, int> = 0> +static inline typename FloatProperties::BitsType valueAsBits(T x) { + using BitsType = typename FloatProperties::BitsType; + return *reinterpret_cast(&x); +} + +// Return the float value from bits. +template >::Value, int> = 0> +static inline FloatTypeT valueFromBits(BitsType bits) { + return *reinterpret_cast *>(&bits); +} + +// Return the bits of abs(x). +template ::Value, int> = 0> +static inline typename FloatProperties::BitsType absBits(T x) { + return valueAsBits(x) & (~FloatProperties::signMask); +} + +// Return the zero adjusted exponent value of x. +template ::Value, int> = 0> +int getExponent(T x) { + using Properties = FloatProperties; + using BitsType = typename Properties::BitsType; + BitsType bits = absBits(x); + int e = (bits >> Properties::mantissaWidth); // Shift out the mantissa. + e -= Properties::exponentOffset; // Zero adjust. + return e; +} + +// Return true if x is infinity (positive or negative.) +template ::Value, int> = 0> +static inline bool isInf(T x) { + using Properties = FloatProperties; + using BitsType = typename FloatProperties::BitsType; + BitsType bits = valueAsBits(x); + return ((bits & BitPatterns::inf) == BitPatterns::inf) && + ((bits & Properties::mantissaMask) == 0); +} + +// Return true if x is a NAN (quiet or signalling.) +template ::Value, int> = 0> +static inline bool isNaN(T x) { + using Properties = FloatProperties; + using BitsType = typename FloatProperties::BitsType; + BitsType bits = valueAsBits(x); + return ((bits & BitPatterns::inf) == BitPatterns::inf) && + ((bits & Properties::mantissaMask) != 0); +} + +// Return true if x is a quiet NAN. +template ::Value, int> = 0> +static inline bool isQuietNaN(T x) { + using Properties = FloatProperties; + using BitsType = typename FloatProperties::BitsType; + BitsType bits = valueAsBits(x); + return ((bits & BitPatterns::inf) == BitPatterns::inf) && + ((bits & Properties::quietNaNMask) != 0); +} + +// Return true if x is a quiet NAN with sign bit set. +template ::Value, int> = 0> +static inline bool isNegativeQuietNaN(T x) { + using Properties = FloatProperties; + using BitsType = typename FloatProperties::BitsType; + BitsType bits = valueAsBits(x); + return ((bits & BitPatterns::negInf) == BitPatterns::negInf) && + ((bits & Properties::quietNaNMask) != 0); +} + +} // namespace fputil +} // namespace __llvm_libc + +#endif // LLVM_LIBC_UTILS_FPUTIL_FLOAT_OPERATIONS_H diff --git a/libc/utils/FPUtil/FloatProperties.h b/libc/utils/FPUtil/FloatProperties.h new file mode 100644 index 000000000000..a9584c7428a1 --- /dev/null +++ b/libc/utils/FPUtil/FloatProperties.h @@ -0,0 +1,72 @@ +//===-- Properties of floating point 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_FLOAT_PROPERTIES_H +#define LLVM_LIBC_UTILS_FPUTIL_FLOAT_PROPERTIES_H + +#include + +namespace __llvm_libc { +namespace fputil { + +template struct FloatProperties {}; + +template <> struct FloatProperties { + typedef uint32_t BitsType; + static_assert(sizeof(BitsType) == sizeof(float), + "Unexpected size of 'float' type."); + + static constexpr uint32_t mantissaWidth = 23; + static constexpr BitsType mantissaMask = 0x007fffffU; + static constexpr BitsType signMask = 0x80000000U; + static constexpr uint32_t exponentOffset = 127; + + // If a number x is a NAN, then it is a quiet NAN if: + // QuietNaNMask & bits(x) != 0 + // Else, it is a signalling NAN. + static constexpr BitsType quietNaNMask = 0x00400000U; +}; + +template <> struct FloatProperties { + typedef uint64_t BitsType; + static_assert(sizeof(BitsType) == sizeof(double), + "Unexpected size of 'double' type."); + + static constexpr uint32_t mantissaWidth = 52; + static constexpr BitsType mantissaMask = 0x000fffffffffffffU; + static constexpr BitsType signMask = 0x8000000000000000ULL; + static constexpr uint32_t exponentOffset = 1023; + + // If a number x is a NAN, then it is a quiet NAN if: + // QuietNaNMask & bits(x) != 0 + // Else, it is a signalling NAN. + static constexpr BitsType quietNaNMask = 0x0008000000000000ULL; +}; + +// Define the float type corresponding to the BitsType. +template struct FloatType; + +template <> struct FloatType { + static_assert(sizeof(uint32_t) == sizeof(float), + "Unexpected size of 'float' type."); + typedef float Type; +}; + +template <> struct FloatType { + static_assert(sizeof(uint64_t) == sizeof(double), + "Unexpected size of 'double' type."); + typedef double Type; +}; + +template +using FloatTypeT = typename FloatType::Type; + +} // namespace fputil +} // namespace __llvm_libc + +#endif // LLVM_LIBC_UTILS_FPUTIL_FLOAT_PROPERTIES_H diff --git a/libc/utils/MPFRWrapper/CMakeLists.txt b/libc/utils/MPFRWrapper/CMakeLists.txt index 218d5af9fc28..6a3c24e27b15 100644 --- a/libc/utils/MPFRWrapper/CMakeLists.txt +++ b/libc/utils/MPFRWrapper/CMakeLists.txt @@ -12,7 +12,7 @@ if(LIBC_TESTS_CAN_USE_MPFR) MPFRUtils.cpp MPFRUtils.h ) - add_dependencies(libcMPFRWrapper libc.utils.CPP.standalone_cpp LibcUnitTest LLVMSupport) + add_dependencies(libcMPFRWrapper libc.utils.CPP.standalone_cpp libc.utils.FPUtil.fputil LibcUnitTest LLVMSupport) target_link_libraries(libcMPFRWrapper -lmpfr -lgmp LibcUnitTest LLVMSupport) else() message(WARNING "Math tests using MPFR will be skipped.") diff --git a/libc/utils/MPFRWrapper/MPFRUtils.cpp b/libc/utils/MPFRWrapper/MPFRUtils.cpp index 980557ad1889..74bb07a94bf6 100644 --- a/libc/utils/MPFRWrapper/MPFRUtils.cpp +++ b/libc/utils/MPFRWrapper/MPFRUtils.cpp @@ -8,6 +8,8 @@ #include "MPFRUtils.h" +#include "utils/FPUtil/FloatOperations.h" + #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" @@ -19,44 +21,6 @@ namespace __llvm_libc { namespace testing { namespace mpfr { -template struct FloatProperties {}; - -template <> struct FloatProperties { - typedef uint32_t BitsType; - static_assert(sizeof(BitsType) == sizeof(float), - "Unexpected size of 'float' type."); - - static constexpr uint32_t mantissaWidth = 23; - static constexpr BitsType signMask = 0x7FFFFFFFU; - static constexpr uint32_t exponentOffset = 127; -}; - -template <> struct FloatProperties { - typedef uint64_t BitsType; - static_assert(sizeof(BitsType) == sizeof(double), - "Unexpected size of 'double' type."); - - static constexpr uint32_t mantissaWidth = 52; - static constexpr BitsType signMask = 0x7FFFFFFFFFFFFFFFULL; - static constexpr uint32_t exponentOffset = 1023; -}; - -template typename FloatProperties::BitsType getBits(T x) { - using BitsType = typename FloatProperties::BitsType; - return *reinterpret_cast(&x); -} - -// Returns the zero adjusted exponent value of abs(x). -template int getExponent(T x) { - using Properties = FloatProperties; - using BitsType = typename Properties::BitsType; - BitsType bits = *reinterpret_cast(&x); - bits &= Properties::signMask; // Zero the sign bit. - int e = (bits >> Properties::mantissaWidth); // Shift out the mantissa. - e -= Properties::exponentOffset; // Zero adjust. - return e; -} - class MPFRNumber { // A precision value which allows sufficiently large additional // precision even compared to double precision floating point values. @@ -94,7 +58,7 @@ public: template MPFRNumber(XType x, const Tolerance &t) { mpfr_init2(value, mpfrPrecision); mpfr_set_zero(value, 1); // Set to positive zero. - MPFRNumber xExponent(getExponent(x)); + MPFRNumber xExponent(fputil::getExponent(x)); // E = 2^E mpfr_exp2(xExponent.value, xExponent.value, MPFR_RNDN); uint32_t bitMask = 1 << (t.width - 1); @@ -170,15 +134,18 @@ namespace internal { template void MPFRMatcher::explainError(testutils::StreamWrapper &OS) { + using fputil::valueAsBits; + MPFRNumber mpfrResult(operation, input); MPFRNumber mpfrInput(input); MPFRNumber mpfrMatchValue(matchValue); MPFRNumber mpfrToleranceValue(matchValue, tolerance); OS << "Match value not within tolerance value of MPFR result:\n" << " Input decimal: " << mpfrInput.str() << '\n' - << " Input bits: 0x" << llvm::utohexstr(getBits(input)) << '\n' + << " Input bits: 0x" << llvm::utohexstr(valueAsBits(input)) << '\n' << " Match decimal: " << mpfrMatchValue.str() << '\n' - << " Match bits: 0x" << llvm::utohexstr(getBits(matchValue)) << '\n' + << " Match bits: 0x" << llvm::utohexstr(valueAsBits(matchValue)) + << '\n' << " MPFR result: " << mpfrResult.str() << '\n' << "Tolerance value: " << mpfrToleranceValue.str() << '\n'; }