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 @@ -152,6 +152,7 @@ let Functions = [ "copysign", "copysignf", + "copysignl", "ceil", "ceilf", "cosf", @@ -162,10 +163,13 @@ "floorf", "frexp", "frexpf", + "frexpl", "logb", "logbf", + "logbl", "modf", "modff", + "modfl", "expf", "exp2f", "round", 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 @@ -48,6 +48,7 @@ # math.h entrypoints libc.src.math.copysign libc.src.math.copysignf + libc.src.math.copysignl libc.src.math.ceil libc.src.math.ceilf libc.src.math.cosf @@ -60,10 +61,13 @@ libc.src.math.floorf libc.src.math.frexp libc.src.math.frexpf + libc.src.math.frexpl libc.src.math.logb libc.src.math.logbf + libc.src.math.logbl libc.src.math.modf libc.src.math.modff + libc.src.math.modfl libc.src.math.round libc.src.math.roundf libc.src.math.sincosf diff --git a/libc/spec/spec.td b/libc/spec/spec.td --- a/libc/spec/spec.td +++ b/libc/spec/spec.td @@ -49,6 +49,7 @@ def ConstVoidPtr : ConstType; def SizeTType : NamedType<"size_t">; def FloatPtr : PtrType; +def LongDoublePtr : PtrType; // _Noreturn is really not a type, but it is convenient to treat it as a type. def NoReturn : NamedType<"_Noreturn void">; diff --git a/libc/spec/stdc.td b/libc/spec/stdc.td --- a/libc/spec/stdc.td +++ b/libc/spec/stdc.td @@ -191,6 +191,7 @@ [ FunctionSpec<"copysign", RetValSpec, [ArgSpec, ArgSpec]>, FunctionSpec<"copysignf", RetValSpec, [ArgSpec, ArgSpec]>, + FunctionSpec<"copysignl", RetValSpec, [ArgSpec, ArgSpec]>, FunctionSpec<"ceil", RetValSpec, [ArgSpec]>, FunctionSpec<"ceilf", RetValSpec, [ArgSpec]>, @@ -204,12 +205,15 @@ FunctionSpec<"frexp", RetValSpec, [ArgSpec, ArgSpec]>, FunctionSpec<"frexpf", RetValSpec, [ArgSpec, ArgSpec]>, + FunctionSpec<"frexpl", RetValSpec, [ArgSpec, ArgSpec]>, FunctionSpec<"logb", RetValSpec, [ArgSpec]>, FunctionSpec<"logbf", RetValSpec, [ArgSpec]>, + FunctionSpec<"logbl", RetValSpec, [ArgSpec]>, FunctionSpec<"modf", RetValSpec, [ArgSpec, ArgSpec]>, FunctionSpec<"modff", RetValSpec, [ArgSpec, ArgSpec]>, + FunctionSpec<"modfl", RetValSpec, [ArgSpec, ArgSpec]>, FunctionSpec<"cosf", RetValSpec, [ArgSpec]>, FunctionSpec<"sinf", 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 @@ -238,6 +238,8 @@ copysign.h DEPENDS libc.utils.FPUtil.fputil + COMPILE_OPTIONS + -O2 ) add_entrypoint_object( @@ -248,6 +250,20 @@ copysignf.h DEPENDS libc.utils.FPUtil.fputil + COMPILE_OPTIONS + -O2 +) + +add_entrypoint_object( + copysignl + SRCS + copysignl.cpp + HDRS + copysignl.h + DEPENDS + libc.utils.FPUtil.fputil + COMPILE_OPTIONS + -O2 ) add_entrypoint_object( @@ -258,6 +274,8 @@ frexp.h DEPENDS libc.utils.FPUtil.fputil + COMPILE_OPTIONS + -O2 ) add_entrypoint_object( @@ -268,6 +286,20 @@ frexpf.h DEPENDS libc.utils.FPUtil.fputil + COMPILE_OPTIONS + -O2 +) + +add_entrypoint_object( + frexpl + SRCS + frexpl.cpp + HDRS + frexpl.h + DEPENDS + libc.utils.FPUtil.fputil + COMPILE_OPTIONS + -O2 ) add_entrypoint_object( @@ -278,6 +310,8 @@ logb.h DEPENDS libc.utils.FPUtil.fputil + COMPILE_OPTIONS + -O2 ) add_entrypoint_object( @@ -288,6 +322,20 @@ logbf.h DEPENDS libc.utils.FPUtil.fputil + COMPILE_OPTIONS + -O2 +) + +add_entrypoint_object( + logbl + SRCS + logbl.cpp + HDRS + logbl.h + DEPENDS + libc.utils.FPUtil.fputil + COMPILE_OPTIONS + -O2 ) add_entrypoint_object( @@ -298,6 +346,8 @@ modf.h DEPENDS libc.utils.FPUtil.fputil + COMPILE_OPTIONS + -O2 ) add_entrypoint_object( @@ -308,4 +358,18 @@ modff.h DEPENDS libc.utils.FPUtil.fputil + COMPILE_OPTIONS + -O2 +) + +add_entrypoint_object( + modfl + SRCS + modfl.cpp + HDRS + modfl.h + DEPENDS + libc.utils.FPUtil.fputil + COMPILE_OPTIONS + -O2 ) diff --git a/libc/src/math/copysignl.h b/libc/src/math/copysignl.h new file mode 100644 --- /dev/null +++ b/libc/src/math/copysignl.h @@ -0,0 +1,18 @@ +//===-- Implementation header for copysignl ---------------------*- 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_SRC_MATH_COPYSIGNL_H +#define LLVM_LIBC_SRC_MATH_COPYSIGNL_H + +namespace __llvm_libc { + +long double copysignl(long double x, long double y); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_MATH_COPYSIGNL_H diff --git a/libc/src/math/copysignl.cpp b/libc/src/math/copysignl.cpp new file mode 100644 --- /dev/null +++ b/libc/src/math/copysignl.cpp @@ -0,0 +1,18 @@ +//===-- Implementation of copysignl function ------------------------------===// +// +// 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 "src/__support/common.h" +#include "utils/FPUtil/ManipulationFunctions.h" + +namespace __llvm_libc { + +long double LLVM_LIBC_ENTRYPOINT(copysignl)(long double x, long double y) { + return fputil::copysign(x, y); +} + +} // namespace __llvm_libc diff --git a/libc/src/math/frexpl.h b/libc/src/math/frexpl.h new file mode 100644 --- /dev/null +++ b/libc/src/math/frexpl.h @@ -0,0 +1,18 @@ +//===-- Implementation header for frexpl ------------------------*- 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_SRC_MATH_FREXPL_H +#define LLVM_LIBC_SRC_MATH_FREXPL_H + +namespace __llvm_libc { + +long double frexpl(long double x, int *exp); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_MATH_FREXPL_H diff --git a/libc/src/math/frexpl.cpp b/libc/src/math/frexpl.cpp new file mode 100644 --- /dev/null +++ b/libc/src/math/frexpl.cpp @@ -0,0 +1,18 @@ +//===-- Implementation of frexpl function ---------------------------------===// +// +// 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 "src/__support/common.h" +#include "utils/FPUtil/ManipulationFunctions.h" + +namespace __llvm_libc { + +long double LLVM_LIBC_ENTRYPOINT(frexpl)(long double x, int *exp) { + return fputil::frexp(x, *exp); +} + +} // namespace __llvm_libc diff --git a/libc/src/math/logbl.h b/libc/src/math/logbl.h new file mode 100644 --- /dev/null +++ b/libc/src/math/logbl.h @@ -0,0 +1,18 @@ +//===-- Implementation header for logbl -------------------------*- 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_SRC_MATH_LOGBL_H +#define LLVM_LIBC_SRC_MATH_LOGBL_H + +namespace __llvm_libc { + +long double logbl(long double x); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_MATH_LOGBL_H diff --git a/libc/src/math/logbl.cpp b/libc/src/math/logbl.cpp new file mode 100644 --- /dev/null +++ b/libc/src/math/logbl.cpp @@ -0,0 +1,18 @@ +//===-- Implementation of logbl function ---------------------------------===// +// +// 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 "src/__support/common.h" +#include "utils/FPUtil/ManipulationFunctions.h" + +namespace __llvm_libc { + +long double LLVM_LIBC_ENTRYPOINT(logbl)(long double x) { + return fputil::logb(x); +} + +} // namespace __llvm_libc diff --git a/libc/src/math/modfl.h b/libc/src/math/modfl.h new file mode 100644 --- /dev/null +++ b/libc/src/math/modfl.h @@ -0,0 +1,18 @@ +//===-- Implementation header for modfl -------------------------*- 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_SRC_MATH_MODFL_H +#define LLVM_LIBC_SRC_MATH_MODFL_H + +namespace __llvm_libc { + +long double modfl(long double x, long double *iptr); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_MATH_MODFL_H diff --git a/libc/src/math/modfl.cpp b/libc/src/math/modfl.cpp new file mode 100644 --- /dev/null +++ b/libc/src/math/modfl.cpp @@ -0,0 +1,18 @@ +//===-- Implementation of modfl function ----------------------------------===// +// +// 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 "src/__support/common.h" +#include "utils/FPUtil/ManipulationFunctions.h" + +namespace __llvm_libc { + +long double LLVM_LIBC_ENTRYPOINT(modfl)(long double x, long double *iptr) { + return fputil::modf(x, *iptr); +} + +} // 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 @@ -280,6 +280,18 @@ ) add_math_unittest( + copysignl_test + SUITE + libc_math_unittests + SRCS + copysignl_test.cpp + DEPENDS + libc.include.math + libc.src.math.copysignl + libc.utils.FPUtil.fputil +) + +add_math_unittest( frexp_test SUITE libc_math_unittests @@ -304,6 +316,18 @@ ) add_math_unittest( + frexpl_test + SUITE + libc_math_unittests + SRCS + frexpl_test.cpp + DEPENDS + libc.include.math + libc.src.math.frexpl + libc.utils.FPUtil.fputil +) + +add_math_unittest( logb_test SUITE libc_math_unittests @@ -328,6 +352,18 @@ ) add_math_unittest( + logbl_test + SUITE + libc_math_unittests + SRCS + logbl_test.cpp + DEPENDS + libc.include.math + libc.src.math.logbl + libc.utils.FPUtil.fputil +) + +add_math_unittest( modf_test SUITE libc_math_unittests @@ -350,3 +386,15 @@ libc.src.math.modff libc.utils.FPUtil.fputil ) + +add_math_unittest( + modfl_test + SUITE + libc_math_unittests + SRCS + modfl_test.cpp + DEPENDS + libc.include.math + libc.src.math.modfl + libc.utils.FPUtil.fputil +) diff --git a/libc/test/src/math/copysignl_test.cpp b/libc/test/src/math/copysignl_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/math/copysignl_test.cpp @@ -0,0 +1,47 @@ +//===-- Unittests for copysignl +//--------------------------------------------===// +// +// 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/copysignl.h" +#include "utils/FPUtil/FPBits.h" +#include "utils/UnitTest/Test.h" + +using FPBits = __llvm_libc::fputil::FPBits; + +TEST(CopysignlTest, SpecialNumbers) { + EXPECT_TRUE(FPBits::negZero() == + __llvm_libc::copysignl(FPBits::zero(), -1.0l)); + EXPECT_TRUE(FPBits::zero() == + __llvm_libc::copysignl(FPBits::negZero(), 1.0l)); + + EXPECT_TRUE(FPBits::negZero() == + __llvm_libc::copysignl(FPBits::zero(), -1.0l)); + EXPECT_TRUE(FPBits::zero() == + __llvm_libc::copysignl(FPBits::negZero(), 1.0l)); + + EXPECT_TRUE( + FPBits(__llvm_libc::copysignl(FPBits::buildNaN(1), -1.0l)).isNaN()); +} + +TEST(CopysignlTest, InDoubleRange) { + 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) || x == 0) + continue; + + long double res1 = __llvm_libc::copysignl(x, -x); + ASSERT_TRUE(res1 == -x); + + long double res2 = __llvm_libc::copysignl(x, x); + ASSERT_TRUE(res2 == x); + } +} diff --git a/libc/test/src/math/frexpl_test.cpp b/libc/test/src/math/frexpl_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/math/frexpl_test.cpp @@ -0,0 +1,105 @@ +//===-- Unittests for frexpl ----------------------------------------------===// +// +// 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/frexpl.h" +#include "utils/FPUtil/BasicOperations.h" +#include "utils/FPUtil/FPBits.h" +#include "utils/UnitTest/Test.h" + +#include + +using FPBits = __llvm_libc::fputil::FPBits; + +TEST(FrexplTest, SpecialNumbers) { + int exponent; + + EXPECT_TRUE(FPBits::inf() == __llvm_libc::frexpl(FPBits::inf(), &exponent)); + EXPECT_TRUE(FPBits::negInf() == + __llvm_libc::frexpl(FPBits::negInf(), &exponent)); + + EXPECT_TRUE(FPBits::zero() == __llvm_libc::frexpl(FPBits::zero(), &exponent)); + EXPECT_EQ(exponent, 0); + + EXPECT_TRUE(FPBits::negZero() == + __llvm_libc::frexpl(FPBits::negZero(), &exponent)); + EXPECT_EQ(exponent, 0); + + EXPECT_TRUE( + FPBits(__llvm_libc::frexpl(FPBits::buildNaN(1), &exponent)).isNaN()); +} + +TEST(FrexplTest, PowersOfTwo) { + int exponent; + + EXPECT_TRUE(0.5l == __llvm_libc::frexpl(1.0l, &exponent)); + EXPECT_EQ(exponent, 1); + EXPECT_TRUE(-0.5l == __llvm_libc::frexpl(-1.0l, &exponent)); + EXPECT_EQ(exponent, 1); + + EXPECT_TRUE(0.5l == __llvm_libc::frexpl(2.0l, &exponent)); + EXPECT_EQ(exponent, 2); + EXPECT_TRUE(-0.5l == __llvm_libc::frexpl(-2.0l, &exponent)); + EXPECT_EQ(exponent, 2); + + EXPECT_TRUE(0.5l == __llvm_libc::frexpl(4.0l, &exponent)); + EXPECT_EQ(exponent, 3); + EXPECT_TRUE(-0.5l == __llvm_libc::frexpl(-4.0l, &exponent)); + EXPECT_EQ(exponent, 3); + + EXPECT_TRUE(0.5l == __llvm_libc::frexpl(8.0l, &exponent)); + EXPECT_EQ(exponent, 4); + EXPECT_TRUE(-0.5l == __llvm_libc::frexpl(-8.0l, &exponent)); + EXPECT_EQ(exponent, 4); + + EXPECT_TRUE(0.5l == __llvm_libc::frexpl(16.0l, &exponent)); + EXPECT_EQ(exponent, 5); + EXPECT_TRUE(-0.5l == __llvm_libc::frexpl(-16.0l, &exponent)); + EXPECT_EQ(exponent, 5); + + EXPECT_TRUE(0.5l == __llvm_libc::frexpl(32.0l, &exponent)); + EXPECT_EQ(exponent, 6); + EXPECT_TRUE(-0.5l == __llvm_libc::frexpl(-32.0l, &exponent)); + EXPECT_EQ(exponent, 6); +} + +TEST(FrexplTest, SomeIntegers) { + int exponent; + + EXPECT_TRUE(0.75l == __llvm_libc::frexpl(24.0l, &exponent)); + EXPECT_EQ(exponent, 5); + EXPECT_TRUE(-0.75l == __llvm_libc::frexpl(-24.0l, &exponent)); + EXPECT_EQ(exponent, 5); + + EXPECT_TRUE(0.625l == __llvm_libc::frexpl(40.0l, &exponent)); + EXPECT_EQ(exponent, 6); + EXPECT_TRUE(-0.625l == __llvm_libc::frexpl(-40.0l, &exponent)); + EXPECT_EQ(exponent, 6); + + EXPECT_TRUE(0.78125l == __llvm_libc::frexpl(800.0l, &exponent)); + EXPECT_EQ(exponent, 10); + EXPECT_TRUE(-0.78125l == __llvm_libc::frexpl(-800.0l, &exponent)); + EXPECT_EQ(exponent, 10); +} + +TEST(FrexplTest, LongDoubleRange) { + 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) || x == 0.0l) + continue; + + int exponent; + long double frac = __llvm_libc::frexpl(x, &exponent); + + ASSERT_TRUE(__llvm_libc::fputil::abs(frac) < 1.0l); + ASSERT_TRUE(__llvm_libc::fputil::abs(frac) >= 0.5l); + } +} diff --git a/libc/test/src/math/logbl_test.cpp b/libc/test/src/math/logbl_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/math/logbl_test.cpp @@ -0,0 +1,77 @@ +//===-- Unittests for logbl -----------------------------------------------===// +// +// 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/logbl.h" +#include "utils/FPUtil/FPBits.h" +#include "utils/FPUtil/ManipulationFunctions.h" +#include "utils/UnitTest/Test.h" + +using FPBits = __llvm_libc::fputil::FPBits; + +TEST(logblTest, SpecialNumbers) { + EXPECT_TRUE(FPBits::inf() == __llvm_libc::logbl(FPBits::inf())); + EXPECT_TRUE(FPBits::inf() == __llvm_libc::logbl(FPBits::negInf())); + + EXPECT_TRUE(FPBits::negInf() == __llvm_libc::logbl(FPBits::zero())); + EXPECT_TRUE(FPBits::negInf() == __llvm_libc::logbl(FPBits::negZero())); + + EXPECT_TRUE(FPBits(__llvm_libc::logbl(FPBits::buildNaN(1))).isNaN()); +} + +TEST(logblTest, PowersOfTwo) { + EXPECT_TRUE(0.0l == __llvm_libc::logbl(1.0l)); + EXPECT_TRUE(0.0l == __llvm_libc::logbl(-1.0l)); + + EXPECT_TRUE(1.0l == __llvm_libc::logbl(2.0l)); + EXPECT_TRUE(1.0l == __llvm_libc::logbl(-2.0l)); + + EXPECT_TRUE(2.0l == __llvm_libc::logbl(4.0l)); + EXPECT_TRUE(2.0l == __llvm_libc::logbl(-4.0l)); + + EXPECT_TRUE(3.0l == __llvm_libc::logbl(8.0l)); + EXPECT_TRUE(3.0l == __llvm_libc::logbl(-8.0l)); + + EXPECT_TRUE(4.0l == __llvm_libc::logbl(16.0l)); + EXPECT_TRUE(4.0l == __llvm_libc::logbl(-16.0l)); + + EXPECT_TRUE(5.0l == __llvm_libc::logbl(32.0l)); + EXPECT_TRUE(5.0l == __llvm_libc::logbl(-32.0l)); +} + +TEST(LogbTest, SomeIntegers) { + EXPECT_TRUE(1.0l == __llvm_libc::logbl(3.0l)); + EXPECT_TRUE(1.0l == __llvm_libc::logbl(-3.0l)); + + EXPECT_TRUE(2.0l == __llvm_libc::logbl(7.0l)); + EXPECT_TRUE(2.0l == __llvm_libc::logbl(-7.0l)); + + EXPECT_TRUE(3.0l == __llvm_libc::logbl(10.0l)); + EXPECT_TRUE(3.0l == __llvm_libc::logbl(-10.0l)); + + EXPECT_TRUE(4.0l == __llvm_libc::logbl(31.0l)); + EXPECT_TRUE(4.0l == __llvm_libc::logbl(-31.0l)); + + EXPECT_TRUE(5.0l == __llvm_libc::logbl(55.0l)); + EXPECT_TRUE(5.0l == __llvm_libc::logbl(-55.0l)); +} + +TEST(LogblTest, LongDoubleRange) { + 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) || x == 0.0l) + continue; + + int exponent; + __llvm_libc::fputil::frexp(x, exponent); + ASSERT_TRUE((long double)(exponent) == __llvm_libc::logbl(x) + 1.0l); + } +} diff --git a/libc/test/src/math/modfl_test.cpp b/libc/test/src/math/modfl_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/math/modfl_test.cpp @@ -0,0 +1,98 @@ +//===-- Unittests for modfl -----------------------------------------------===// +// +// 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/modfl.h" +#include "utils/FPUtil/BasicOperations.h" +#include "utils/FPUtil/FPBits.h" +#include "utils/FPUtil/NearestIntegerOperations.h" +#include "utils/UnitTest/Test.h" + +using FPBits = __llvm_libc::fputil::FPBits; + +TEST(modflTest, SpecialNumbers) { + long double integral; + + EXPECT_TRUE(FPBits::zero() == __llvm_libc::modfl(FPBits::inf(), &integral)); + EXPECT_TRUE(FPBits::inf() == integral); + + EXPECT_TRUE(FPBits::negZero() == + __llvm_libc::modfl(FPBits::negInf(), &integral)); + EXPECT_TRUE(FPBits::negInf() == integral); + + EXPECT_TRUE(FPBits::zero() == __llvm_libc::modfl(FPBits::zero(), &integral)); + EXPECT_TRUE(integral == 0.0l); + + EXPECT_TRUE(FPBits::negZero() == + __llvm_libc::modfl(FPBits::negZero(), &integral)); + EXPECT_TRUE(integral == 0.0l); + + EXPECT_TRUE( + FPBits(__llvm_libc::modfl(FPBits::buildNaN(1), &integral)).isNaN()); +} + +TEST(modflTest, Integers) { + long double integral; + + EXPECT_TRUE(FPBits::zero() == __llvm_libc::modfl(1.0l, &integral)); + EXPECT_TRUE(integral == 1.0l); + + EXPECT_TRUE(FPBits::negZero() == __llvm_libc::modfl(-1.0l, &integral)); + EXPECT_TRUE(integral == -1.0l); + + EXPECT_TRUE(FPBits::zero() == __llvm_libc::modfl(10.0l, &integral)); + EXPECT_TRUE(integral == 10.0l); + + EXPECT_TRUE(FPBits::negZero() == __llvm_libc::modfl(-10.0l, &integral)); + EXPECT_TRUE(integral == -10.0l); + + EXPECT_TRUE(FPBits::zero() == __llvm_libc::modfl(12345.0l, &integral)); + EXPECT_TRUE(integral == 12345.0l); + + EXPECT_TRUE(FPBits::negZero() == __llvm_libc::modfl(-12345.0l, &integral)); + EXPECT_TRUE(integral == -12345.0l); +} + +TEST(ModfTest, Fractions) { + long double integral; + + EXPECT_TRUE(0.5l == __llvm_libc::modfl(1.5l, &integral)); + EXPECT_TRUE(integral == 1.0l); + + EXPECT_TRUE(-0.5l == __llvm_libc::modfl(-1.5l, &integral)); + EXPECT_TRUE(integral == -1.0l); + + EXPECT_TRUE(0.75l == __llvm_libc::modfl(10.75l, &integral)); + EXPECT_TRUE(integral == 10.0l); + + EXPECT_TRUE(-0.75l == __llvm_libc::modfl(-10.75l, &integral)); + EXPECT_TRUE(integral == -10.0l); + + EXPECT_TRUE(0.125l == __llvm_libc::modfl(100.125l, &integral)); + EXPECT_TRUE(integral == 100.0l); + + EXPECT_TRUE(-0.125l == __llvm_libc::modfl(-100.125l, &integral)); + EXPECT_TRUE(integral == -100.0l); +} + +TEST(ModflTest, LongDoubleRange) { + 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) || x == 0.0l) + continue; + + long double integral; + long double frac = __llvm_libc::modfl(x, &integral); + ASSERT_TRUE(__llvm_libc::fputil::abs(frac) < 1.0l); + ASSERT_TRUE(__llvm_libc::fputil::trunc(x) == integral); + ASSERT_TRUE(integral + frac == x); + } +} diff --git a/libc/utils/FPUtil/ManipulationFunctions.h b/libc/utils/FPUtil/ManipulationFunctions.h --- a/libc/utils/FPUtil/ManipulationFunctions.h +++ b/libc/utils/FPUtil/ManipulationFunctions.h @@ -6,10 +6,7 @@ // //===----------------------------------------------------------------------===// -#include "BitPatterns.h" -#include "ClassificationFunctions.h" -#include "FloatOperations.h" -#include "FloatProperties.h" +#include "FPBits.h" #include "NearestIntegerOperations.h" #include "utils/CPP/TypeTraits.h" @@ -20,52 +17,87 @@ namespace __llvm_libc { namespace fputil { -template ::Value, int> = 0> +#if defined(__x86_64__) || defined(__i386__) +template struct Standard754Type { + static constexpr bool Value = + cpp::IsSame>::Value || + cpp::IsSame>::Value; +}; +#else +template struct Standard754Type { + static constexpr bool Value = cpp::IsFloatingPointType::Value; +}; +#endif + +template static inline T frexp_impl(FPBits &bits, int &exp) { + exp = bits.getExponent() + 1; + static constexpr uint16_t resultExponent = FPBits::exponentBias - 1; + bits.exponent = resultExponent; + return bits; +} + +template ::Value, int> = 0> static inline T frexp(T x, int &exp) { - using Properties = FloatProperties; - using BitsType = typename Properties::BitsType; + FPBits bits(x); + if (bits.isInfOrNaN()) + return x; + if (bits.isZero()) { + exp = 0; + return x; + } + + return frexp_impl(bits, exp); +} - auto bits = valueAsBits(x); - if (bitsAreInfOrNaN(bits)) +#if defined(__x86_64__) || defined(__i386__) +static inline long double frexp(long double x, int &exp) { + FPBits bits(x); + if (bits.isInfOrNaN()) return x; - if (bitsAreZero(bits)) { + if (bits.isZero()) { exp = 0; return x; } - exp = getExponentFromBits(bits) + 1; + if (bits.exponent != 0 || bits.implicitBit == 1) + return frexp_impl(bits, exp); - static constexpr BitsType resultExponent = - Properties::exponentOffset - BitsType(1); - // Capture the sign and mantissa part. - bits &= (Properties::mantissaMask | Properties::signMask); - // Insert the new exponent. - bits |= (resultExponent << Properties::mantissaWidth); + exp = bits.getExponent(); + int shiftCount = 0; + uint64_t fullMantissa = *reinterpret_cast(&bits); + static constexpr uint64_t msBitMask = uint64_t(1) << 63; + for (; (fullMantissa & msBitMask) == uint64_t(0); + fullMantissa <<= 1, ++shiftCount) { + // This for loop will terminate as fullMantissa is != 0. If it were 0, + // then x will be NaN and handled before control reaches here. + // When the loop terminates, fullMantissa will represent the full mantissa + // of a normal long double value. That is, the implicit bit has the value + // of 1. + } - return valueFromBits(bits); + exp = exp - shiftCount + 1; + *reinterpret_cast(&bits) = fullMantissa; + bits.exponent = FPBits::exponentBias - 1; + return bits; } +#endif template ::Value, int> = 0> static inline T modf(T x, T &iptr) { - auto bits = valueAsBits(x); - if (bitsAreZero(bits) || bitsAreNaN(bits)) { + FPBits bits(x); + if (bits.isZero() || bits.isNaN()) { iptr = x; return x; - } else if (bitsAreInf(bits)) { + } else if (bits.isInf()) { iptr = x; - return bits & FloatProperties::signMask - ? valueFromBits(BitPatterns::negZero) - : valueFromBits(BitPatterns::zero); + return bits.sign ? FPBits::negZero() : FPBits::zero(); } else { iptr = trunc(x); if (x == iptr) { // If x is already an integer value, then return zero with the right // sign. - return bits & FloatProperties::signMask - ? valueFromBits(BitPatterns::negZero) - : valueFromBits(BitPatterns::zero); + return bits.sign ? FPBits::negZero() : FPBits::zero(); } else { return x - iptr; } @@ -75,28 +107,65 @@ template ::Value, int> = 0> static inline T copysign(T x, T y) { - constexpr auto signMask = FloatProperties::signMask; - auto xbits = valueAsBits(x); - auto ybits = valueAsBits(y); - return valueFromBits((xbits & ~signMask) | (ybits & signMask)); + FPBits xbits(x); + xbits.sign = FPBits(y).sign; + return xbits; } -template ::Value, int> = 0> +template static inline T logb_impl(const FPBits &bits) { + return bits.getExponent(); +} + +template ::Value, int> = 0> static inline T logb(T x) { - auto bits = valueAsBits(x); - if (bitsAreZero(bits)) { + FPBits bits(x); + if (bits.isZero()) { // TODO(Floating point exception): Raise div-by-zero exception. // TODO(errno): POSIX requires setting errno to ERANGE. - return valueFromBits(BitPatterns::negInf); - } else if (bitsAreInf(bits)) { - return valueFromBits(BitPatterns::inf); - } else if (bitsAreNaN(bits)) { + return FPBits::negInf(); + } else if (bits.isNaN()) { return x; - } else { - return getExponentFromBits(bits); + } else if (bits.isInf()) { + // Return positive infinity. + return FPBits::inf(); + } + + return logb_impl(bits); +} + +#if defined(__x86_64__) || defined(__i386__) +static inline long double logb(long double x) { + FPBits bits(x); + if (bits.isZero()) { + // TODO(Floating point exception): Raise div-by-zero exception. + // TODO(errno): POSIX requires setting errno to ERANGE. + return FPBits::negInf(); + } else if (bits.isNaN()) { + return x; + } else if (bits.isInf()) { + // Return positive infinity. + return FPBits::inf(); } + + if (bits.exponent != 0 || bits.implicitBit == 1) + return logb_impl(bits); + + int exp = bits.getExponent(); + int shiftCount = 0; + uint64_t fullMantissa = *reinterpret_cast(&bits); + static constexpr uint64_t msBitMask = uint64_t(1) << 63; + for (; (fullMantissa & msBitMask) == uint64_t(0); + fullMantissa <<= 1, ++shiftCount) { + // This for loop will terminate as fullMantissa is != 0. If it were 0, + // then x will be NaN and handled before control reaches here. + // When the loop terminates, fullMantissa will represent the full mantissa + // of a normal long double value. That is, the implicit bit has the value + // of 1. + } + + return exp - shiftCount; } +#endif } // namespace fputil } // namespace __llvm_libc