diff --git a/libc/config/darwin/arm/entrypoints.txt b/libc/config/darwin/arm/entrypoints.txt index 9eb7d8960c6e49..383118dc781e55 100644 --- a/libc/config/darwin/arm/entrypoints.txt +++ b/libc/config/darwin/arm/entrypoints.txt @@ -123,6 +123,7 @@ set(TARGET_LIBM_ENTRYPOINTS libc.src.math.atan2f libc.src.math.atanf libc.src.math.atanhf + libc.src.math.cbrtf libc.src.math.copysign libc.src.math.copysignf libc.src.math.copysignl diff --git a/libc/config/gpu/entrypoints.txt b/libc/config/gpu/entrypoints.txt index c8d68d61f3212d..62f3f0df247ccc 100644 --- a/libc/config/gpu/entrypoints.txt +++ b/libc/config/gpu/entrypoints.txt @@ -242,6 +242,7 @@ set(TARGET_LIBM_ENTRYPOINTS libc.src.math.atanf libc.src.math.atanh libc.src.math.atanhf + libc.src.math.cbrtf libc.src.math.ceil libc.src.math.ceilf libc.src.math.copysign diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt index 6c67e4bbadee7e..2e4ac7b98c8ecc 100644 --- a/libc/config/linux/aarch64/entrypoints.txt +++ b/libc/config/linux/aarch64/entrypoints.txt @@ -343,6 +343,7 @@ set(TARGET_LIBM_ENTRYPOINTS libc.src.math.atan2f libc.src.math.atanf libc.src.math.atanhf + libc.src.math.cbrtf libc.src.math.ceil libc.src.math.ceilf libc.src.math.ceill diff --git a/libc/config/linux/arm/entrypoints.txt b/libc/config/linux/arm/entrypoints.txt index a24514e29334d7..b0ee0e989b5edd 100644 --- a/libc/config/linux/arm/entrypoints.txt +++ b/libc/config/linux/arm/entrypoints.txt @@ -216,6 +216,7 @@ set(TARGET_LIBM_ENTRYPOINTS libc.src.math.atan2f libc.src.math.atanf libc.src.math.atanhf + libc.src.math.cbrtf libc.src.math.ceil libc.src.math.ceilf libc.src.math.ceill diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt index 2b7e3d0256fc3a..37f5764547401c 100644 --- a/libc/config/linux/riscv/entrypoints.txt +++ b/libc/config/linux/riscv/entrypoints.txt @@ -345,6 +345,7 @@ set(TARGET_LIBM_ENTRYPOINTS libc.src.math.atan2f libc.src.math.atanf libc.src.math.atanhf + libc.src.math.cbrtf libc.src.math.ceil libc.src.math.ceilf libc.src.math.ceill diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt index 2ca8f00d2de50a..57a6a1f53375a6 100644 --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -368,6 +368,7 @@ set(TARGET_LIBM_ENTRYPOINTS libc.src.math.canonicalize libc.src.math.canonicalizef libc.src.math.canonicalizel + libc.src.math.cbrtf libc.src.math.ceil libc.src.math.ceilf libc.src.math.ceill diff --git a/libc/config/windows/entrypoints.txt b/libc/config/windows/entrypoints.txt index b33d572cf999ac..499c6bfe3a229c 100644 --- a/libc/config/windows/entrypoints.txt +++ b/libc/config/windows/entrypoints.txt @@ -121,6 +121,7 @@ set(TARGET_LIBM_ENTRYPOINTS libc.src.math.atan2f libc.src.math.atanf libc.src.math.atanhf + libc.src.math.cbrtf libc.src.math.copysign libc.src.math.copysignf libc.src.math.copysignl diff --git a/libc/docs/math/index.rst b/libc/docs/math/index.rst index 422acfcdd4cec2..7914a3d7e6d1a9 100644 --- a/libc/docs/math/index.rst +++ b/libc/docs/math/index.rst @@ -266,7 +266,7 @@ Higher Math Functions +-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+ | atanpi | | | | | | 7.12.4.10 | F.10.1.10 | +-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+ -| cbrt | | | | | | 7.12.7.1 | F.10.4.1 | +| cbrt | |check| | | | | | 7.12.7.1 | F.10.4.1 | +-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+ | compoundn | | | | | | 7.12.7.2 | F.10.4.2 | +-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+ diff --git a/libc/spec/stdc.td b/libc/spec/stdc.td index feaa3fbfa66aa5..aa56152aee1413 100644 --- a/libc/spec/stdc.td +++ b/libc/spec/stdc.td @@ -382,6 +382,8 @@ def StdC : StandardSpec<"stdc"> { ], [], // Enumerations [ + FunctionSpec<"cbrtf", RetValSpec, [ArgSpec]>, + FunctionSpec<"copysign", RetValSpec, [ArgSpec, ArgSpec]>, FunctionSpec<"copysignf", RetValSpec, [ArgSpec, ArgSpec]>, FunctionSpec<"copysignl", RetValSpec, [ArgSpec, ArgSpec]>, diff --git a/libc/src/__support/FPUtil/CMakeLists.txt b/libc/src/__support/FPUtil/CMakeLists.txt index 84c5f802710c4a..793d3a121c7427 100644 --- a/libc/src/__support/FPUtil/CMakeLists.txt +++ b/libc/src/__support/FPUtil/CMakeLists.txt @@ -155,6 +155,8 @@ add_header_library( multiply_add.h DEPENDS libc.src.__support.common + FLAGS + FMA_OPT ) add_header_library( diff --git a/libc/src/__support/FPUtil/FEnvImpl.h b/libc/src/__support/FPUtil/FEnvImpl.h index 13e668becc651a..1674dd0358e883 100644 --- a/libc/src/__support/FPUtil/FEnvImpl.h +++ b/libc/src/__support/FPUtil/FEnvImpl.h @@ -67,6 +67,12 @@ LIBC_INLINE int set_env(const fenv_t *) { return 0; } namespace LIBC_NAMESPACE::fputil { +LIBC_INLINE int clear_except_if_required(int excepts) { + if (math_errhandling & MATH_ERREXCEPT) + return clear_except(excepts); + return 0; +} + LIBC_INLINE int set_except_if_required(int excepts) { if (math_errhandling & MATH_ERREXCEPT) return set_except(excepts); diff --git a/libc/src/math/CMakeLists.txt b/libc/src/math/CMakeLists.txt index e21011f37b53c4..74c2e4efda617e 100644 --- a/libc/src/math/CMakeLists.txt +++ b/libc/src/math/CMakeLists.txt @@ -65,6 +65,8 @@ add_math_entrypoint_object(canonicalizel) add_math_entrypoint_object(canonicalizef16) add_math_entrypoint_object(canonicalizef128) +add_math_entrypoint_object(cbrtf) + add_math_entrypoint_object(ceil) add_math_entrypoint_object(ceilf) add_math_entrypoint_object(ceill) diff --git a/libc/src/math/cbrtf.h b/libc/src/math/cbrtf.h new file mode 100644 index 00000000000000..74744594f29864 --- /dev/null +++ b/libc/src/math/cbrtf.h @@ -0,0 +1,18 @@ +//===-- Implementation header for cbrtf -------------------------*- 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_CBRTF_H +#define LLVM_LIBC_SRC_MATH_CBRTF_H + +namespace LIBC_NAMESPACE { + +float cbrtf(float x); + +} // namespace LIBC_NAMESPACE + +#endif // LLVM_LIBC_SRC_MATH_CBRTF_H diff --git a/libc/src/math/generic/CMakeLists.txt b/libc/src/math/generic/CMakeLists.txt index fc7d6996af1e6c..e2bbdcfe5a15b8 100644 --- a/libc/src/math/generic/CMakeLists.txt +++ b/libc/src/math/generic/CMakeLists.txt @@ -4092,3 +4092,19 @@ add_entrypoint_object( COMPILE_OPTIONS -O3 ) + +add_entrypoint_object( + cbrtf + SRCS + cbrtf.cpp + HDRS + ../cbrtf.h + COMPILE_OPTIONS + -O3 + DEPENDS + libc.hdr.fenv_macros + libc.src.__support.FPUtil.fenv_impl + libc.src.__support.FPUtil.fp_bits + libc.src.__support.FPUtil.multiply_add + libc.src.__support.macros.optimization +) diff --git a/libc/src/math/generic/cbrtf.cpp b/libc/src/math/generic/cbrtf.cpp new file mode 100644 index 00000000000000..a1eb58d463a065 --- /dev/null +++ b/libc/src/math/generic/cbrtf.cpp @@ -0,0 +1,157 @@ +//===-- Implementation of cbrtf 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/cbrtf.h" +#include "hdr/fenv_macros.h" +#include "src/__support/FPUtil/FEnvImpl.h" +#include "src/__support/FPUtil/FPBits.h" +#include "src/__support/FPUtil/multiply_add.h" +#include "src/__support/common.h" +#include "src/__support/macros/optimization.h" // LIBC_UNLIKELY + +namespace LIBC_NAMESPACE { + +namespace { + +// Look up table for 2^(i/3) for i = 0, 1, 2. +constexpr double CBRT2[3] = {1.0, 0x1.428a2f98d728bp0, 0x1.965fea53d6e3dp0}; + +// Degree-7 polynomials approximation of ((1 + x)^(1/3) - 1)/x for 0 <= x <= 1 +// generated by Sollya with: +// > for i from 0 to 15 do { +// P = fpminimax((1 + x)^(1/3) - 1)/x, 6, [|D...|], [i/16, (i + 1)/16]); +// print("{", coeff(P, 0), ",", coeff(P, 1), ",", coeff(P, 2), ",", +// coeff(P, 3), ",", coeff(P, 4), ",", coeff(P, 5), ",", +// coeff(P, 6), "},"); +// }; +// Then (1 + x)^(1/3) ~ 1 + x * P(x). +constexpr double COEFFS[16][7] = { + {0x1.55555555554ebp-2, -0x1.c71c71c678c0cp-4, 0x1.f9add2776de81p-5, + -0x1.511e10aa964a7p-5, 0x1.ee44165937fa2p-6, -0x1.7c5c9e059345dp-6, + 0x1.047f75e0aff14p-6}, + {0x1.5555554d1149ap-2, -0x1.c71c676fcb5bp-4, 0x1.f9ab127dc57ebp-5, + -0x1.50ea8fd1d4c15p-5, 0x1.e9d68f28ced43p-6, -0x1.60e0e1e661311p-6, + 0x1.716eca1d6e3bcp-7}, + {0x1.5555546377d45p-2, -0x1.c71bc1c6d49d2p-4, 0x1.f9924cc0ed24dp-5, + -0x1.4fea3beb53b3bp-5, 0x1.de028a9a07b1bp-6, -0x1.3b090d2233524p-6, + 0x1.0aeca34893785p-7}, + {0x1.55554dce9f649p-2, -0x1.c7188b34b98f8p-4, 0x1.f93e1af34af49p-5, + -0x1.4d9a06be75c63p-5, 0x1.cb943f4f68992p-6, -0x1.139a685a5e3c4p-6, + 0x1.88410674c6a5dp-8}, + {0x1.5555347d211c3p-2, -0x1.c70f2a4b1a5fap-4, 0x1.f88420e8602c3p-5, + -0x1.49becfa4ed3ep-5, 0x1.b475cd9013162p-6, -0x1.dcfee1dd2f8efp-7, + 0x1.249bb51a1c498p-8}, + {0x1.5554f01b33dbap-2, -0x1.c6facb929dbf1p-4, 0x1.f73fb7861252ep-5, + -0x1.4459a4a0071fap-5, 0x1.9a8df2b504fc2p-6, -0x1.9a7ce3006d06ep-7, + 0x1.ba9230918fa2ep-9}, + {0x1.55545c695db5fp-2, -0x1.c6d6089f20275p-4, 0x1.f556e0ea80efp-5, + -0x1.3d91372d083f4p-5, 0x1.7f66cff331f4p-6, -0x1.606a562491737p-7, + 0x1.52e3e17c71069p-9}, + {0x1.55534a879232ap-2, -0x1.c69b836998b84p-4, 0x1.f2bb26dac0e4cp-5, + -0x1.359eed43716d7p-5, 0x1.64218cd824fbcp-6, -0x1.2e703e2e091e8p-7, + 0x1.0677d9af6aad4p-9}, + {0x1.5551836bb5494p-2, -0x1.c64658c15353bp-4, 0x1.ef68517451a6ep-5, + -0x1.2cc20a980dceep-5, 0x1.49843e0fad93ap-6, -0x1.03c59ccb68e54p-7, + 0x1.9ad325dc7adcbp-10}, + {0x1.554ecacb0d035p-2, -0x1.c5d2664026ffcp-4, 0x1.eb624796ba809p-5, + -0x1.233803d19a535p-5, 0x1.300decb1c3c28p-6, -0x1.befe18031ec3dp-8, + 0x1.449f5ee175c69p-10}, + {0x1.554ae1f5ae815p-2, -0x1.c53c6b14ff6b2p-4, 0x1.e6b2d5127bb5bp-5, + -0x1.19387336788a3p-5, 0x1.180955a6ab255p-6, -0x1.81696703ba369p-8, + 0x1.02cb36389bd79p-10}, + {0x1.55458a59f356ep-2, -0x1.c4820dd631ae9p-4, 0x1.e167af818bd15p-5, + -0x1.0ef35f6f72e52p-5, 0x1.019c33b65e4ebp-6, -0x1.4d25bdd52d3a5p-8, + 0x1.a008ae91f5936p-11}, + {0x1.553e878eafee1p-2, -0x1.c3a1d0b2a3db2p-4, 0x1.db90d8ed9f89bp-5, + -0x1.0490e20f1ae91p-5, 0x1.d9a5d1fc42fe3p-7, -0x1.20bf8227c2abfp-8, + 0x1.50f8174cdb6e9p-11}, + {0x1.5535a0dedf1b1p-2, -0x1.c29afb8bd01a1p-4, 0x1.d53f6371c1e27p-5, + -0x1.f463209b433e2p-6, 0x1.b35222a17e44p-7, -0x1.f5efbf505e133p-9, + 0x1.12e0e94e8586dp-11}, + {0x1.552aa25e57bfdp-2, -0x1.c16d811e4acadp-4, 0x1.ce8489b47aa51p-5, + -0x1.dfde7ff758ea8p-6, 0x1.901f43aac38c8p-7, -0x1.b581d07df5ad5p-9, + 0x1.c3726535f1fc6p-12}, + {0x1.551d5d9b204d3p-2, -0x1.c019e328f8db1p-4, 0x1.c7710f44fc3cep-5, + -0x1.cbbbe25ea8ba4p-6, 0x1.6fe270088623dp-7, -0x1.7e6fc79733761p-9, + 0x1.75077abf18d84p-12}, +}; + +} // anonymous namespace + +LLVM_LIBC_FUNCTION(float, cbrtf, (float x)) { + using FloatBits = typename fputil::FPBits; + using DoubleBits = typename fputil::FPBits; + + FloatBits x_bits(x); + + uint32_t x_abs = x_bits.uintval() & 0x7fff'ffff; + uint32_t sign_bit = (x_bits.uintval() >> 31) << DoubleBits::EXP_LEN; + + if (LIBC_UNLIKELY(x_abs == 0 || x_abs >= 0x7f80'0000)) { + // x is 0, Inf, or NaN. + return x; + } + + double xd = static_cast(x); + DoubleBits xd_bits(xd); + + // When using biased exponent of x in double precision, + // x_e = real_exponent_of_x + 1023 + // Then: + // x_e / 3 = real_exponent_of_x / 3 + 1023/3 + // = real_exponent_of_x / 3 + 341 + // So to make it the correct biased exponent of x^(1/3), we add + // 1023 - 341 = 682 + // to the quotient x_e / 3. + unsigned x_e = static_cast(xd_bits.get_biased_exponent()); + unsigned out_e = (x_e / 3 + 682) | sign_bit; + unsigned shift_e = x_e % 3; + + // Set x_m = 2^(x_e % 3) * (1.mantissa) + uint64_t x_m = xd_bits.get_mantissa(); + // Use the leading 4 bits for look up table + unsigned idx = static_cast(x_m >> (DoubleBits::FRACTION_LEN - 4)); + + x_m |= static_cast(DoubleBits::EXP_BIAS) + << DoubleBits::FRACTION_LEN; + + double x_reduced = DoubleBits(x_m).get_val(); + double dx = x_reduced - 1.0; + + double dx_sq = dx * dx; + double c0 = fputil::multiply_add(dx, COEFFS[idx][0], 1.0); + double c1 = fputil::multiply_add(dx, COEFFS[idx][2], COEFFS[idx][1]); + double c2 = fputil::multiply_add(dx, COEFFS[idx][4], COEFFS[idx][3]); + double c3 = fputil::multiply_add(dx, COEFFS[idx][6], COEFFS[idx][5]); + + double dx_4 = dx_sq * dx_sq; + double p0 = fputil::multiply_add(dx_sq, c1, c0); + double p1 = fputil::multiply_add(dx_sq, c3, c2); + + double r = fputil::multiply_add(dx_4, p1, p0) * CBRT2[shift_e]; + + uint64_t r_m = DoubleBits(r).get_mantissa(); + // Check if the output is exact. To be exact, the smallest 1-bit of the + // output has to be at least 2^-7 or higher. So we check the lowest 44 bits + // to see if they are within 2^(-52 + 3) errors from all zeros, then the + // result cube root is exact. + if (LIBC_UNLIKELY(((r_m + 8) & 0xfffffffffff) <= 16)) { + if ((r_m & 0xfffffffffff) <= 8) + r_m &= 0xffff'ffff'ffff'ffe0; + else + r_m = (r_m & 0xffff'ffff'ffff'ffe0) + 0x20; + fputil::clear_except_if_required(FE_INEXACT); + } + // Adjust exponent and sign. + uint64_t r_bits = + r_m | (static_cast(out_e) << DoubleBits::FRACTION_LEN); + + return static_cast(DoubleBits(r_bits).get_val()); +} + +} // namespace LIBC_NAMESPACE diff --git a/libc/test/src/math/CMakeLists.txt b/libc/test/src/math/CMakeLists.txt index 35ca97b5de8af0..0dc7ae6aae2df6 100644 --- a/libc/test/src/math/CMakeLists.txt +++ b/libc/test/src/math/CMakeLists.txt @@ -2213,6 +2213,18 @@ add_fp_unittest( libc.src.math.f16sqrtl ) +add_fp_unittest( + cbrtf_test + NEED_MPFR + SUITE + libc-math-unittests + SRCS + cbrtf_test.cpp + DEPENDS + libc.src.math.cbrtf + libc.src.__support.FPUtil.fp_bits +) + add_subdirectory(generic) add_subdirectory(smoke) diff --git a/libc/test/src/math/cbrtf_test.cpp b/libc/test/src/math/cbrtf_test.cpp new file mode 100644 index 00000000000000..1d7d2189d52681 --- /dev/null +++ b/libc/test/src/math/cbrtf_test.cpp @@ -0,0 +1,42 @@ +//===-- Unittests for cbrtf -----------------------------------------------===// +// +// 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 "hdr/math_macros.h" +#include "src/__support/FPUtil/FPBits.h" +#include "src/math/cbrtf.h" +#include "test/UnitTest/FPMatcher.h" +#include "test/UnitTest/Test.h" +#include "utils/MPFRWrapper/MPFRUtils.h" + +using LlvmLibcCbrtfTest = LIBC_NAMESPACE::testing::FPTest; + +namespace mpfr = LIBC_NAMESPACE::testing::mpfr; + +TEST_F(LlvmLibcCbrtfTest, InFloatRange) { + constexpr uint32_t COUNT = 100'000; + const uint32_t STEP = FPBits(inf).uintval() / COUNT; + for (uint32_t i = 0, v = 0; i <= COUNT; ++i, v += STEP) { + float x = FPBits(v).get_val(); + EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Cbrt, x, + LIBC_NAMESPACE::cbrtf(x), 0.5); + EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Cbrt, -x, + LIBC_NAMESPACE::cbrtf(-x), 0.5); + } +} + +TEST_F(LlvmLibcCbrtfTest, SpecialValues) { + constexpr float INPUTS[] = { + 0x1.60451p2f, 0x1.31304cp1f, 0x1.d17cp2f, 0x1.bp-143f, 0x1.338cp2f, + }; + for (float v : INPUTS) { + float x = FPBits(v).get_val(); + mpfr::ForceRoundingMode r(mpfr::RoundingMode::Upward); + EXPECT_MPFR_MATCH(mpfr::Operation::Cbrt, x, LIBC_NAMESPACE::cbrtf(x), 0.5, + mpfr::RoundingMode::Upward); + } +} diff --git a/libc/test/src/math/exhaustive/CMakeLists.txt b/libc/test/src/math/exhaustive/CMakeLists.txt index c5f75b51cbd9f6..6c10ea422109e7 100644 --- a/libc/test/src/math/exhaustive/CMakeLists.txt +++ b/libc/test/src/math/exhaustive/CMakeLists.txt @@ -486,3 +486,19 @@ add_fp_unittest( LINK_LIBRARIES -lpthread ) + +add_fp_unittest( + cbrtf_test + NO_RUN_POSTBUILD + NEED_MPFR + SUITE + libc_math_exhaustive_tests + SRCS + cbrtf_test.cpp + DEPENDS + .exhaustive_test + libc.src.math.cbrtf + libc.src.__support.FPUtil.fp_bits + LINK_LIBRARIES + -lpthread +) diff --git a/libc/test/src/math/exhaustive/cbrtf_test.cpp b/libc/test/src/math/exhaustive/cbrtf_test.cpp new file mode 100644 index 00000000000000..e4511e1f7ee1b9 --- /dev/null +++ b/libc/test/src/math/exhaustive/cbrtf_test.cpp @@ -0,0 +1,33 @@ +//===-- Exhaustive test for cbrtf -----------------------------------------===// +// +// 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 "exhaustive_test.h" +#include "src/math/cbrtf.h" +#include "utils/MPFRWrapper/MPFRUtils.h" + +namespace mpfr = LIBC_NAMESPACE::testing::mpfr; + +using LlvmLibcCbrtfExhaustiveTest = + LlvmLibcUnaryOpExhaustiveMathTest; + +// Range: [0, Inf]; +static constexpr uint32_t POS_START = 0x0000'0000U; +static constexpr uint32_t POS_STOP = 0x7f80'0000U; + +TEST_F(LlvmLibcCbrtfExhaustiveTest, PostiveRange) { + test_full_range_all_roundings(POS_START, POS_STOP); +} + +// Range: [-Inf, 0]; +static constexpr uint32_t NEG_START = 0x8000'0000U; +static constexpr uint32_t NEG_STOP = 0xff80'0000U; + +TEST_F(LlvmLibcCbrtfExhaustiveTest, NegativeRange) { + test_full_range_all_roundings(NEG_START, NEG_STOP); +} diff --git a/libc/test/src/math/exhaustive/exhaustive_test.h b/libc/test/src/math/exhaustive/exhaustive_test.h index 6f0c78ebefa470..94489d2e55daa7 100644 --- a/libc/test/src/math/exhaustive/exhaustive_test.h +++ b/libc/test/src/math/exhaustive/exhaustive_test.h @@ -61,9 +61,9 @@ struct UnaryOpChecker : public virtual LIBC_NAMESPACE::testing::Test { TEST_MPFR_MATCH_ROUNDING_SILENTLY(Op, x, Func(x), 0.5, rounding); failed += (!correct); // Uncomment to print out failed values. - // if (!correct) { - // EXPECT_MPFR_MATCH_ROUNDING(Op, x, Func(x), 0.5, rounding); - // } + if (!correct) { + EXPECT_MPFR_MATCH_ROUNDING(Op, x, Func(x), 0.5, rounding); + } } while (bits++ < stop); return failed; } @@ -97,9 +97,9 @@ struct BinaryOpChecker : public virtual LIBC_NAMESPACE::testing::Test { 0.5, rounding); failed += (!correct); // Uncomment to print out failed values. - // if (!correct) { - // EXPECT_MPFR_MATCH_ROUNDING(Op, input, Func(x, y), 0.5, rounding); - // } + if (!correct) { + EXPECT_MPFR_MATCH_ROUNDING(Op, input, Func(x, y), 0.5, rounding); + } } while (ybits++ < y_stop); } while (xbits++ < x_stop); return failed; @@ -187,7 +187,8 @@ struct LlvmLibcExhaustiveMathTest std::stringstream msg; msg << "Test failed for " << std::dec << failed_in_range << " inputs in range: "; - explain_failed_range(msg, start, stop, extra_range_bounds...); + explain_failed_range(msg, range_begin, range_end, + extra_range_bounds...); msg << "\n"; std::cerr << msg.str() << std::flush; diff --git a/libc/test/src/math/smoke/CMakeLists.txt b/libc/test/src/math/smoke/CMakeLists.txt index b72d4b30787a0d..7f1bc0c204c68c 100644 --- a/libc/test/src/math/smoke/CMakeLists.txt +++ b/libc/test/src/math/smoke/CMakeLists.txt @@ -3961,3 +3961,13 @@ add_fp_unittest( DEPENDS libc.src.math.tan ) + +add_fp_unittest( + cbrtf_test + SUITE + libc-math-smoke-tests + SRCS + cbrtf_test.cpp + DEPENDS + libc.src.math.cbrtf +) diff --git a/libc/test/src/math/smoke/cbrtf_test.cpp b/libc/test/src/math/smoke/cbrtf_test.cpp new file mode 100644 index 00000000000000..a68e57744bd0e7 --- /dev/null +++ b/libc/test/src/math/smoke/cbrtf_test.cpp @@ -0,0 +1,33 @@ +//===-- Unittests for cbrtf -----------------------------------------------===// +// +// 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/cbrtf.h" +#include "test/UnitTest/FPMatcher.h" +#include "test/UnitTest/Test.h" + +using LlvmLibcCbrtfTest = LIBC_NAMESPACE::testing::FPTest; + +using LIBC_NAMESPACE::testing::tlog; + +TEST_F(LlvmLibcCbrtfTest, SpecialNumbers) { + EXPECT_FP_EQ_ALL_ROUNDING(aNaN, LIBC_NAMESPACE::cbrtf(aNaN)); + EXPECT_FP_EQ_ALL_ROUNDING(inf, LIBC_NAMESPACE::cbrtf(inf)); + EXPECT_FP_EQ_ALL_ROUNDING(neg_inf, LIBC_NAMESPACE::cbrtf(neg_inf)); + EXPECT_FP_EQ_ALL_ROUNDING(zero, LIBC_NAMESPACE::cbrtf(zero)); + EXPECT_FP_EQ_ALL_ROUNDING(neg_zero, LIBC_NAMESPACE::cbrtf(neg_zero)); + EXPECT_FP_EQ_ALL_ROUNDING(1.0f, LIBC_NAMESPACE::cbrtf(1.0f)); + EXPECT_FP_EQ_ALL_ROUNDING(-1.0f, LIBC_NAMESPACE::cbrtf(-1.0f)); + EXPECT_FP_EQ_ALL_ROUNDING(2.0f, LIBC_NAMESPACE::cbrtf(8.0f)); + EXPECT_FP_EQ_ALL_ROUNDING(-2.0f, LIBC_NAMESPACE::cbrtf(-8.0f)); + EXPECT_FP_EQ_ALL_ROUNDING(3.0f, LIBC_NAMESPACE::cbrtf(27.0f)); + EXPECT_FP_EQ_ALL_ROUNDING(-3.0f, LIBC_NAMESPACE::cbrtf(-27.0f)); + EXPECT_FP_EQ_ALL_ROUNDING(5.0f, LIBC_NAMESPACE::cbrtf(125.0f)); + EXPECT_FP_EQ_ALL_ROUNDING(-5.0f, LIBC_NAMESPACE::cbrtf(-125.0f)); + EXPECT_FP_EQ_ALL_ROUNDING(0x1.0p42f, LIBC_NAMESPACE::cbrtf(0x1.0p126f)); + EXPECT_FP_EQ_ALL_ROUNDING(-0x1.0p42f, LIBC_NAMESPACE::cbrtf(-0x1.0p126f)); +} diff --git a/libc/utils/MPFRWrapper/MPFRUtils.cpp b/libc/utils/MPFRWrapper/MPFRUtils.cpp index 6548fc36cb6b4e..99a240e555af25 100644 --- a/libc/utils/MPFRWrapper/MPFRUtils.cpp +++ b/libc/utils/MPFRWrapper/MPFRUtils.cpp @@ -221,6 +221,12 @@ class MPFRNumber { return result; } + MPFRNumber cbrt() const { + MPFRNumber result(*this); + mpfr_cbrt(result.value, value, mpfr_rounding); + return result; + } + MPFRNumber ceil() const { MPFRNumber result(*this); mpfr_ceil(result.value, value); @@ -702,6 +708,8 @@ unary_operation(Operation op, InputType input, unsigned int precision, return mpfrInput.atan(); case Operation::Atanh: return mpfrInput.atanh(); + case Operation::Cbrt: + return mpfrInput.cbrt(); case Operation::Ceil: return mpfrInput.ceil(); case Operation::Cos: diff --git a/libc/utils/MPFRWrapper/MPFRUtils.h b/libc/utils/MPFRWrapper/MPFRUtils.h index 002dc919396e72..fd0d72472bf7ed 100644 --- a/libc/utils/MPFRWrapper/MPFRUtils.h +++ b/libc/utils/MPFRWrapper/MPFRUtils.h @@ -31,6 +31,7 @@ enum class Operation : int { Asinh, Atan, Atanh, + Cbrt, Ceil, Cos, Cosh,