diff --git a/libc/test/CMakeLists.txt b/libc/test/CMakeLists.txt --- a/libc/test/CMakeLists.txt +++ b/libc/test/CMakeLists.txt @@ -3,3 +3,4 @@ add_subdirectory(config) add_subdirectory(loader) add_subdirectory(src) +add_subdirectory(utils) diff --git a/libc/test/utils/CMakeLists.txt b/libc/test/utils/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/libc/test/utils/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(FPUtil) diff --git a/libc/test/utils/FPUtil/CMakeLists.txt b/libc/test/utils/FPUtil/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/libc/test/utils/FPUtil/CMakeLists.txt @@ -0,0 +1,10 @@ +if((${LIBC_TARGET_OS} STREQUAL "linux") AND (${LIBC_TARGET_MACHINE} MATCHES "i386|x86_64")) + add_libc_unittest( + x86_long_double_test + SRCS + x86_long_double_test.cpp + DEPENDS + libc.include.math + libc.utils.FPUtil.fputil + ) +endif() diff --git a/libc/test/utils/FPUtil/x86_long_double_test.cpp b/libc/test/utils/FPUtil/x86_long_double_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/utils/FPUtil/x86_long_double_test.cpp @@ -0,0 +1,85 @@ +//===-- Unittests for x86 long double -------------------------------------===// +// +// 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 "utils/FPUtil/FPBits.h" +#include "utils/UnitTest/Test.h" + +using FPBits = __llvm_libc::fputil::FPBits; + +TEST(X86LongDoubleTest, isNaN) { + // In the nan checks below, we use the macro isnan from math.h to ensure that + // a number is actually a NaN. The isnan macro resolves to the compiler + // builtin function. Hence, matching LLVM-libc's notion of NaN with the + // isnan result ensures that LLVM-libc's behavior matches the compiler's + // behavior. + + FPBits bits(0.0l); + bits.exponent = FPBits::maxExponent; + for (unsigned int i = 0; i < 1000000; ++i) { + // If exponent has the max value and the implicit bit is 0, + // then the number is a NaN for all values of mantissa. + bits.mantissa = i; + long double nan = bits; + ASSERT_NE(isnan(nan), 0); + ASSERT_TRUE(bits.isNaN()); + } + + bits.implicitBit = 1; + for (unsigned int i = 1; i < 1000000; ++i) { + // If exponent has the max value and the implicit bit is 1, + // then the number is a NaN for all non-zero values of mantissa. + // Note the initial value of |i| of 1 to avoid a zero mantissa. + bits.mantissa = i; + long double nan = bits; + ASSERT_NE(isnan(nan), 0); + ASSERT_TRUE(bits.isNaN()); + } + + bits.exponent = 1; + bits.implicitBit = 0; + for (unsigned int i = 0; i < 1000000; ++i) { + // If exponent is non-zero and also not max, and the implicit bit is 0, + // then the number is a NaN for all values of mantissa. + bits.mantissa = i; + long double nan = bits; + ASSERT_NE(isnan(nan), 0); + ASSERT_TRUE(bits.isNaN()); + } + + bits.exponent = 1; + bits.implicitBit = 1; + for (unsigned int i = 0; i < 1000000; ++i) { + // If exponent is non-zero and also not max, and the implicit bit is 1, + // then the number is normal value for all values of mantissa. + bits.mantissa = i; + long double valid = bits; + ASSERT_EQ(isnan(valid), 0); + ASSERT_FALSE(bits.isNaN()); + } + + bits.exponent = 0; + bits.implicitBit = 1; + for (unsigned int i = 0; i < 1000000; ++i) { + // If exponent is zero, then the number is a valid but denormal value. + bits.mantissa = i; + long double valid = bits; + ASSERT_EQ(isnan(valid), 0); + ASSERT_FALSE(bits.isNaN()); + } + + bits.exponent = 0; + bits.implicitBit = 0; + for (unsigned int i = 0; i < 1000000; ++i) { + // If exponent is zero, then the number is a valid but denormal value. + bits.mantissa = i; + long double valid = bits; + ASSERT_EQ(isnan(valid), 0); + ASSERT_FALSE(bits.isNaN()); + } +} diff --git a/libc/utils/FPUtil/LongDoubleBitsX86.h b/libc/utils/FPUtil/LongDoubleBitsX86.h --- a/libc/utils/FPUtil/LongDoubleBitsX86.h +++ b/libc/utils/FPUtil/LongDoubleBitsX86.h @@ -62,9 +62,18 @@ return exponent == maxExponent && mantissa == 0 && implicitBit == 1; } - bool isNaN() const { return exponent == maxExponent && mantissa != 0; } + 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; } + bool isInfOrNaN() const { + return (exponent == maxExponent) || (exponent != 0 && implicitBit == 0); + } // Methods below this are used by tests.