diff --git a/libc/config/darwin/arm/entrypoints.txt b/libc/config/darwin/arm/entrypoints.txt --- a/libc/config/darwin/arm/entrypoints.txt +++ b/libc/config/darwin/arm/entrypoints.txt @@ -198,6 +198,9 @@ libc.src.math.round libc.src.math.roundf libc.src.math.roundl + libc.src.math.scalbn + libc.src.math.scalbnf + libc.src.math.scalbnl libc.src.math.sincosf libc.src.math.sinhf libc.src.math.sinf 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 @@ -300,6 +300,9 @@ libc.src.math.round libc.src.math.roundf libc.src.math.roundl + libc.src.math.scalbn + libc.src.math.scalbnf + libc.src.math.scalbnl libc.src.math.sincosf libc.src.math.sinhf libc.src.math.sinf 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 @@ -302,6 +302,9 @@ libc.src.math.round libc.src.math.roundf libc.src.math.roundl + libc.src.math.scalbn + libc.src.math.scalbnf + libc.src.math.scalbnl libc.src.math.sin libc.src.math.sincosf libc.src.math.sinhf diff --git a/libc/config/windows/entrypoints.txt b/libc/config/windows/entrypoints.txt --- a/libc/config/windows/entrypoints.txt +++ b/libc/config/windows/entrypoints.txt @@ -202,6 +202,9 @@ libc.src.math.round libc.src.math.roundf libc.src.math.roundl + libc.src.math.scalbn + libc.src.math.scalbnf + libc.src.math.scalbnl libc.src.math.sin libc.src.math.sincosf libc.src.math.sinf diff --git a/libc/docs/math.rst b/libc/docs/math.rst --- a/libc/docs/math.rst +++ b/libc/docs/math.rst @@ -110,7 +110,7 @@ remquo :green:`XA` :green:`XA` :green:`XA` rint :green:`XA` :green:`XA` :green:`XA` round :green:`XA` :green:`XA` :green:`XA` -scalbn +scalbn :green:`XA` :green:`XA` :green:`XA` signbit trunc :green:`XA` :green:`XA` :green:`XA` ============== ================ =============== ====================== diff --git a/libc/spec/stdc.td b/libc/spec/stdc.td --- a/libc/spec/stdc.td +++ b/libc/spec/stdc.td @@ -495,6 +495,10 @@ FunctionSpec<"acoshf", RetValSpec, [ArgSpec]>, FunctionSpec<"asinhf", RetValSpec, [ArgSpec]>, FunctionSpec<"atanhf", RetValSpec, [ArgSpec]>, + + FunctionSpec<"scalbn", RetValSpec, [ArgSpec, ArgSpec]>, + FunctionSpec<"scalbnf", RetValSpec, [ArgSpec, ArgSpec]>, + FunctionSpec<"scalbnl", RetValSpec, [ArgSpec, ArgSpec]>, ] >; diff --git a/libc/src/__support/FPUtil/ManipulationFunctions.h b/libc/src/__support/FPUtil/ManipulationFunctions.h --- a/libc/src/__support/FPUtil/ManipulationFunctions.h +++ b/libc/src/__support/FPUtil/ManipulationFunctions.h @@ -116,8 +116,10 @@ template , int> = 0> LIBC_INLINE T ldexp(T x, int exp) { + if (unlikely(exp == 0)) + return x; FPBits bits(x); - if (bits.is_zero() || bits.is_inf_or_nan() || exp == 0) + if (unlikely(bits.is_zero() || bits.is_inf_or_nan())) return x; // NormalFloat uses int32_t to store the true exponent value. We should ensure 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 @@ -192,6 +192,10 @@ add_math_entrypoint_object(roundf) add_math_entrypoint_object(roundl) +add_math_entrypoint_object(scalbn) +add_math_entrypoint_object(scalbnf) +add_math_entrypoint_object(scalbnl) + add_math_entrypoint_object(sincosf) add_math_entrypoint_object(sin) diff --git a/libc/src/math/generic/CMakeLists.txt b/libc/src/math/generic/CMakeLists.txt --- a/libc/src/math/generic/CMakeLists.txt +++ b/libc/src/math/generic/CMakeLists.txt @@ -1433,3 +1433,39 @@ COMPILE_OPTIONS -O3 ) + +add_entrypoint_object( + scalbn + SRCS + scalbn.cpp + HDRS + ../scalbn.h + DEPENDS + libc.src.__support.FPUtil.manipulation_functions + COMPILE_OPTIONS + -O3 +) + +add_entrypoint_object( + scalbnf + SRCS + scalbnf.cpp + HDRS + ../scalbnf.h + DEPENDS + libc.src.__support.FPUtil.manipulation_functions + COMPILE_OPTIONS + -O3 +) + +add_entrypoint_object( + scalbnl + SRCS + scalbnl.cpp + HDRS + ../scalbnl.h + DEPENDS + libc.src.__support.FPUtil.manipulation_functions + COMPILE_OPTIONS + -O3 +) diff --git a/libc/src/math/generic/scalbn.cpp b/libc/src/math/generic/scalbn.cpp new file mode 100644 --- /dev/null +++ b/libc/src/math/generic/scalbn.cpp @@ -0,0 +1,25 @@ +//===-- Single-precision scalbn 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/math/scalbn.h" +#include "src/__support/FPUtil/ManipulationFunctions.h" +#include "src/__support/common.h" + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(double, scalbn, (double x, int n)) { +#if !defined(__FLT_RADIX__) +#error __FLT_RADIX__ undefined. +#elif __FLT_RADIX__ != 2 +#error __FLT_RADIX__!=2, unimplemented. +#else + return fputil::ldexp(x, n); +#endif +} + +} // namespace __llvm_libc diff --git a/libc/src/math/generic/scalbnf.cpp b/libc/src/math/generic/scalbnf.cpp new file mode 100644 --- /dev/null +++ b/libc/src/math/generic/scalbnf.cpp @@ -0,0 +1,25 @@ +//===-- Single-precision scalbnf 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/math/scalbnf.h" +#include "src/__support/FPUtil/ManipulationFunctions.h" +#include "src/__support/common.h" + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(float, scalbnf, (float x, int n)) { +#if !defined(__FLT_RADIX__) +#error __FLT_RADIX__ undefined. +#elif __FLT_RADIX__ != 2 +#error __FLT_RADIX__!=2, unimplemented. +#else + return fputil::ldexp(x, n); +#endif +} + +} // namespace __llvm_libc diff --git a/libc/src/math/generic/scalbnl.cpp b/libc/src/math/generic/scalbnl.cpp new file mode 100644 --- /dev/null +++ b/libc/src/math/generic/scalbnl.cpp @@ -0,0 +1,25 @@ +//===-- Single-precision scalbnl 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/math/scalbnl.h" +#include "src/__support/FPUtil/ManipulationFunctions.h" +#include "src/__support/common.h" + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(long double, scalbnl, (long double x, int n)) { +#if !defined(__FLT_RADIX__) +#error __FLT_RADIX__ undefined. +#elif __FLT_RADIX__ != 2 +#error __FLT_RADIX__!=2, unimplemented. +#else + return fputil::ldexp(x, n); +#endif +} + +} // namespace __llvm_libc diff --git a/libc/src/math/scalbn.h b/libc/src/math/scalbn.h new file mode 100644 --- /dev/null +++ b/libc/src/math/scalbn.h @@ -0,0 +1,18 @@ +//===-- Implementation header for scalbn ------------------------*- 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_SCALBN_H +#define LLVM_LIBC_SRC_MATH_SCALBN_H + +namespace __llvm_libc { + +double scalbn(double x, int n); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_MATH_SCALBN_H diff --git a/libc/src/math/scalbnf.h b/libc/src/math/scalbnf.h new file mode 100644 --- /dev/null +++ b/libc/src/math/scalbnf.h @@ -0,0 +1,18 @@ +//===-- Implementation header for scalbnf -----------------------*- 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_SCALBNF_H +#define LLVM_LIBC_SRC_MATH_SCALBNF_H + +namespace __llvm_libc { + +float scalbnf(float x, int n); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_MATH_SCALBNF_H diff --git a/libc/src/math/scalbnl.h b/libc/src/math/scalbnl.h new file mode 100644 --- /dev/null +++ b/libc/src/math/scalbnl.h @@ -0,0 +1,18 @@ +//===-- Implementation header for scalbnl -----------------------*- 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_SCALBNL_H +#define LLVM_LIBC_SRC_MATH_SCALBNL_H + +namespace __llvm_libc { + +long double scalbnl(long double x, int n); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_MATH_SCALBNL_H 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 @@ -1574,9 +1574,57 @@ SRCS pow_test.cpp DEPENDS - libc.include.errno - libc.src.errno.errno - libc.src.math.pow + libc.include.errno + libc.src.errno.errno + libc.src.math.pow +) + +add_fp_unittest( + scalbn_test + NEED_MPFR + SUITE + libc_math_unittests + SRCS + scalbn_test.cpp + HDRS + ScalbnTest.h + DEPENDS + libc.include.math + libc.src.math.scalbn + libc.src.__support.FPUtil.fp_bits + libc.src.__support.FPUtil.normal_float +) + +add_fp_unittest( + scalbnf_test + NEED_MPFR + SUITE + libc_math_unittests + SRCS + scalbnf_test.cpp + HDRS + ScalbnTest.h + DEPENDS + libc.include.math + libc.src.math.scalbnf + libc.src.__support.FPUtil.fp_bits + libc.src.__support.FPUtil.normal_float +) + +add_fp_unittest( + scalbnl_test + NEED_MPFR + SUITE + libc_math_unittests + SRCS + scalbnl_test.cpp + HDRS + ScalbnTest.h + DEPENDS + libc.include.math + libc.src.math.scalbnl + libc.src.__support.FPUtil.fp_bits + libc.src.__support.FPUtil.normal_float ) add_subdirectory(generic) diff --git a/libc/test/src/math/ScalbnTest.h b/libc/test/src/math/ScalbnTest.h new file mode 100644 --- /dev/null +++ b/libc/test/src/math/ScalbnTest.h @@ -0,0 +1,28 @@ +//===-- Utility class to test different flavors of scalbn -------*- 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_SCALBN_H +#define LLVM_LIBC_TEST_SRC_MATH_SCALBN_H + +#include "LdExpTest.h" +#include "utils/UnitTest/Test.h" + +#define LIST_SCALBN_TESTS(T, func) \ + using LlvmLibcScalbnTest = LdExpTestTemplate; \ + TEST_F(LlvmLibcScalbnTest, SpecialNumbers) { testSpecialNumbers(&func); } \ + TEST_F(LlvmLibcScalbnTest, PowersOfTwo) { testPowersOfTwo(&func); } \ + TEST_F(LlvmLibcScalbnTest, OverFlow) { testOverflow(&func); } \ + TEST_F(LlvmLibcScalbnTest, UnderflowToZeroOnNormal) { \ + testUnderflowToZeroOnNormal(&func); \ + } \ + TEST_F(LlvmLibcScalbnTest, UnderflowToZeroOnSubnormal) { \ + testUnderflowToZeroOnSubnormal(&func); \ + } \ + TEST_F(LlvmLibcScalbnTest, NormalOperation) { testNormalOperation(&func); } + +#endif // LLVM_LIBC_TEST_SRC_MATH_SCALBN_H diff --git a/libc/test/src/math/scalbn_test.cpp b/libc/test/src/math/scalbn_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/math/scalbn_test.cpp @@ -0,0 +1,13 @@ +//===-- Unittests for scalbn ----------------------------------------------===// +// +// 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 "ScalbnTest.h" + +#include "src/math/scalbn.h" + +LIST_SCALBN_TESTS(double, __llvm_libc::scalbn) diff --git a/libc/test/src/math/scalbnf_test.cpp b/libc/test/src/math/scalbnf_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/math/scalbnf_test.cpp @@ -0,0 +1,13 @@ +//===-- Unittests for scalbnf ---------------------------------------------===// +// +// 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 "ScalbnTest.h" + +#include "src/math/scalbnf.h" + +LIST_SCALBN_TESTS(float, __llvm_libc::scalbnf) diff --git a/libc/test/src/math/scalbnl_test.cpp b/libc/test/src/math/scalbnl_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/math/scalbnl_test.cpp @@ -0,0 +1,13 @@ +//===-- Unittests for scalbnl ---------------------------------------------===// +// +// 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 "ScalbnTest.h" + +#include "src/math/scalbnl.h" + +LIST_SCALBN_TESTS(long double, __llvm_libc::scalbnl)