[libc-commits] [libc] [libc][math][c++23] Add exp2bf16 math function (PR #230213)

via libc-commits libc-commits at lists.llvm.org
Thu Oct 15 08:05:37 PDT 2026


https://github.com/un-pixelated updated https://github.com/llvm/llvm-project/pull/230213

>From 6ac907bc4764457406357d50377129597a6f7779 Mon Sep 17 00:00:00 2001
From: pixl <planner.chisels.7j at icloud.com>
Date: Thu, 15 Oct 2026 20:33:11 +0530
Subject: [PATCH] [libc][math][c++23] Add exp2bf16 math function

---
 libc/config/baremetal/aarch64/entrypoints.txt |   1 +
 libc/config/baremetal/arm/entrypoints.txt     |   1 +
 libc/config/baremetal/riscv/entrypoints.txt   |   1 +
 libc/config/darwin/aarch64/entrypoints.txt    |   1 +
 libc/config/darwin/x86_64/entrypoints.txt     |   1 +
 libc/config/gpu/amdgpu/entrypoints.txt        |   1 +
 libc/config/gpu/nvptx/entrypoints.txt         |   1 +
 libc/config/linux/aarch64/entrypoints.txt     |   1 +
 libc/config/linux/arm/entrypoints.txt         |   1 +
 libc/config/linux/riscv/entrypoints.txt       |   1 +
 libc/config/linux/x86_64/entrypoints.txt      |   1 +
 libc/config/windows/entrypoints.txt           |   1 +
 libc/shared/math.h                            |   1 +
 libc/shared/math/exp2bf16.h                   |  28 +++
 libc/src/__support/math/CMakeLists.txt        |  21 ++
 libc/src/__support/math/exp2bf16.h            | 211 ++++++++++++++++++
 libc/src/math/CMakeLists.txt                  |   1 +
 libc/src/math/exp2bf16.h                      |  26 +++
 libc/src/math/generic/CMakeLists.txt          |  10 +
 libc/src/math/generic/exp2bf16.cpp            |  23 ++
 libc/test/shared/shared_math_test.cpp         |   1 +
 libc/test/src/math/CMakeLists.txt             |  12 +
 libc/test/src/math/exp2bf16_test.cpp          |  46 ++++
 libc/test/src/math/smoke/CMakeLists.txt       |  13 ++
 libc/test/src/math/smoke/exp2bf16_test.cpp    |  66 ++++++
 25 files changed, 471 insertions(+)
 create mode 100644 libc/shared/math/exp2bf16.h
 create mode 100644 libc/src/__support/math/exp2bf16.h
 create mode 100644 libc/src/math/exp2bf16.h
 create mode 100644 libc/src/math/generic/exp2bf16.cpp
 create mode 100644 libc/test/src/math/exp2bf16_test.cpp
 create mode 100644 libc/test/src/math/smoke/exp2bf16_test.cpp

diff --git a/libc/config/baremetal/aarch64/entrypoints.txt b/libc/config/baremetal/aarch64/entrypoints.txt
index 114534ce61ae6e..cb341c2d8b0e30 100644
--- a/libc/config/baremetal/aarch64/entrypoints.txt
+++ b/libc/config/baremetal/aarch64/entrypoints.txt
@@ -828,6 +828,7 @@ list(APPEND TARGET_LIBM_ENTRYPOINTS
   libc.src.math.canonicalizebf16
   libc.src.math.ceilbf16
   libc.src.math.copysignbf16
+  libc.src.math.exp2bf16
   libc.src.math.expbf16
   libc.src.math.fabsbf16
   libc.src.math.fdimbf16
diff --git a/libc/config/baremetal/arm/entrypoints.txt b/libc/config/baremetal/arm/entrypoints.txt
index c8148815c34e34..65e36c110d696f 100644
--- a/libc/config/baremetal/arm/entrypoints.txt
+++ b/libc/config/baremetal/arm/entrypoints.txt
@@ -839,6 +839,7 @@ list(APPEND TARGET_LIBM_ENTRYPOINTS
   libc.src.math.canonicalizebf16
   libc.src.math.ceilbf16
   libc.src.math.copysignbf16
+  libc.src.math.exp2bf16
   libc.src.math.expbf16
   libc.src.math.fabsbf16
   libc.src.math.fdimbf16
diff --git a/libc/config/baremetal/riscv/entrypoints.txt b/libc/config/baremetal/riscv/entrypoints.txt
index 622d6156cff11d..caa5f3c559cc6c 100644
--- a/libc/config/baremetal/riscv/entrypoints.txt
+++ b/libc/config/baremetal/riscv/entrypoints.txt
@@ -831,6 +831,7 @@ list(APPEND TARGET_LIBM_ENTRYPOINTS
   libc.src.math.canonicalizebf16
   libc.src.math.ceilbf16
   libc.src.math.copysignbf16
+  libc.src.math.exp2bf16
   libc.src.math.expbf16
   libc.src.math.fabsbf16
   libc.src.math.fdimbf16
diff --git a/libc/config/darwin/aarch64/entrypoints.txt b/libc/config/darwin/aarch64/entrypoints.txt
index d702b2c5a65497..5600b82ad3410b 100644
--- a/libc/config/darwin/aarch64/entrypoints.txt
+++ b/libc/config/darwin/aarch64/entrypoints.txt
@@ -650,6 +650,7 @@ list(APPEND TARGET_LIBM_ENTRYPOINTS
   libc.src.math.canonicalizebf16
   libc.src.math.ceilbf16
   libc.src.math.copysignbf16
+  libc.src.math.exp2bf16
   libc.src.math.expbf16
   libc.src.math.fabsbf16
   libc.src.math.fdimbf16
diff --git a/libc/config/darwin/x86_64/entrypoints.txt b/libc/config/darwin/x86_64/entrypoints.txt
index 716ab572f8d3cd..e3fc845d2ed3af 100644
--- a/libc/config/darwin/x86_64/entrypoints.txt
+++ b/libc/config/darwin/x86_64/entrypoints.txt
@@ -285,6 +285,7 @@ list(APPEND TARGET_LIBM_ENTRYPOINTS
   libc.src.math.canonicalizebf16
   libc.src.math.ceilbf16
   libc.src.math.copysignbf16
+  libc.src.math.exp2bf16
   libc.src.math.expbf16
   libc.src.math.fabsbf16
   libc.src.math.fdimbf16
diff --git a/libc/config/gpu/amdgpu/entrypoints.txt b/libc/config/gpu/amdgpu/entrypoints.txt
index 5c5e5891da8227..2eaef608e9a7ac 100644
--- a/libc/config/gpu/amdgpu/entrypoints.txt
+++ b/libc/config/gpu/amdgpu/entrypoints.txt
@@ -699,6 +699,7 @@ list(APPEND TARGET_LIBM_ENTRYPOINTS
   libc.src.math.canonicalizebf16
   libc.src.math.ceilbf16
   libc.src.math.copysignbf16
+  libc.src.math.exp2bf16
   libc.src.math.expbf16
   libc.src.math.fabsbf16
   libc.src.math.fdimbf16
diff --git a/libc/config/gpu/nvptx/entrypoints.txt b/libc/config/gpu/nvptx/entrypoints.txt
index afae725484a891..79d0c733ec3c01 100644
--- a/libc/config/gpu/nvptx/entrypoints.txt
+++ b/libc/config/gpu/nvptx/entrypoints.txt
@@ -715,6 +715,7 @@ list(APPEND TARGET_LIBM_ENTRYPOINTS
   libc.src.math.canonicalizebf16
   libc.src.math.ceilbf16
   libc.src.math.copysignbf16
+  libc.src.math.exp2bf16
   libc.src.math.expbf16
   libc.src.math.fabsbf16
   libc.src.math.fdimbf16
diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index cee039a405c8d3..42ce377d7dca9a 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -1063,6 +1063,7 @@ list(APPEND TARGET_LIBM_ENTRYPOINTS
   libc.src.math.canonicalizebf16
   libc.src.math.ceilbf16
   libc.src.math.copysignbf16
+  libc.src.math.exp2bf16
   libc.src.math.expbf16
   libc.src.math.fabsbf16
   libc.src.math.fdimbf16
diff --git a/libc/config/linux/arm/entrypoints.txt b/libc/config/linux/arm/entrypoints.txt
index a6ed087f34ee2a..85af57dc2c8d23 100644
--- a/libc/config/linux/arm/entrypoints.txt
+++ b/libc/config/linux/arm/entrypoints.txt
@@ -664,6 +664,7 @@ list(APPEND TARGET_LIBM_ENTRYPOINTS
   libc.src.math.canonicalizebf16
   libc.src.math.ceilbf16
   libc.src.math.copysignbf16
+  libc.src.math.exp2bf16
   libc.src.math.expbf16
   libc.src.math.fabsbf16
   libc.src.math.fdimbf16
diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt
index f91a8939ac61d6..7e0511487c2cf5 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -1137,6 +1137,7 @@ list(APPEND TARGET_LIBM_ENTRYPOINTS
   libc.src.math.canonicalizebf16
   libc.src.math.ceilbf16
   libc.src.math.copysignbf16
+  libc.src.math.exp2bf16
   libc.src.math.expbf16
   libc.src.math.fabsbf16
   libc.src.math.fdimbf16
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 1aaafc9c084679..155ae28d753bec 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -1159,6 +1159,7 @@ list(APPEND TARGET_LIBM_ENTRYPOINTS
   libc.src.math.canonicalizebf16
   libc.src.math.ceilbf16
   libc.src.math.copysignbf16
+  libc.src.math.exp2bf16
   libc.src.math.expbf16
   libc.src.math.fabsbf16
   libc.src.math.fdimbf16
diff --git a/libc/config/windows/entrypoints.txt b/libc/config/windows/entrypoints.txt
index 3a48b49e169b3a..1712b2ad26fda4 100644
--- a/libc/config/windows/entrypoints.txt
+++ b/libc/config/windows/entrypoints.txt
@@ -376,6 +376,7 @@ list(APPEND TARGET_LIBM_ENTRYPOINTS
   libc.src.math.canonicalizebf16
   libc.src.math.ceilbf16
   libc.src.math.copysignbf16
+  libc.src.math.exp2bf16
   libc.src.math.expbf16
   libc.src.math.fabsbf16
   libc.src.math.fdimbf16
diff --git a/libc/shared/math.h b/libc/shared/math.h
index 7b52cedf99c245..eb72bebcb53bfa 100644
--- a/libc/shared/math.h
+++ b/libc/shared/math.h
@@ -110,6 +110,7 @@
 #include "math/exp10m1f.h"
 #include "math/exp10m1f16.h"
 #include "math/exp2.h"
+#include "math/exp2bf16.h"
 #include "math/exp2f.h"
 #include "math/exp2f16.h"
 #include "math/exp2m1f.h"
diff --git a/libc/shared/math/exp2bf16.h b/libc/shared/math/exp2bf16.h
new file mode 100644
index 00000000000000..fa7a760c14e529
--- /dev/null
+++ b/libc/shared/math/exp2bf16.h
@@ -0,0 +1,28 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Shared header for the exp2bf16 function.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SHARED_MATH_EXP2BF16_H
+#define LLVM_LIBC_SHARED_MATH_EXP2BF16_H
+
+#include "shared/libc_common.h"
+#include "src/__support/math/exp2bf16.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace shared {
+
+using math::exp2bf16;
+
+} // namespace shared
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SHARED_MATH_EXP2BF16_H
diff --git a/libc/src/__support/math/CMakeLists.txt b/libc/src/__support/math/CMakeLists.txt
index ef33e3f8ada7b0..cd17eb93c93425 100644
--- a/libc/src/__support/math/CMakeLists.txt
+++ b/libc/src/__support/math/CMakeLists.txt
@@ -4295,6 +4295,27 @@ add_header_library(
     libc.src.__support.macros.optimization
 )
 
+add_header_library(
+  exp2bf16
+  HDRS
+    exp2bf16.h
+  DEPENDS
+    libc.hdr.errno_macros
+    libc.hdr.fenv_macros
+    libc.src.__support.FPUtil.bfloat16
+    libc.src.__support.FPUtil.cast
+    libc.src.__support.FPUtil.fenv_impl
+    libc.src.__support.FPUtil.fp_bits
+    libc.src.__support.FPUtil.multiply_add
+    libc.src.__support.FPUtil.nearest_integer
+    libc.src.__support.FPUtil.polyeval
+    libc.src.__support.FPUtil.rounding_mode
+    libc.src.__support.macros.attributes
+    libc.src.__support.macros.config
+    libc.src.__support.macros.optimization
+    libc.src.__support.macros.properties.types
+)
+
 add_header_library(
   exp2f16
   HDRS
diff --git a/libc/src/__support/math/exp2bf16.h b/libc/src/__support/math/exp2bf16.h
new file mode 100644
index 00000000000000..44fa5da0ebe477
--- /dev/null
+++ b/libc/src/__support/math/exp2bf16.h
@@ -0,0 +1,211 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Implementation of the exp2bf16 function, which computes 2^x for bfloat16.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_MATH_EXP2BF16_H
+#define LLVM_LIBC_SRC___SUPPORT_MATH_EXP2BF16_H
+
+#include "hdr/errno_macros.h"
+#include "hdr/fenv_macros.h"
+#include "src/__support/FPUtil/FEnvImpl.h"
+#include "src/__support/FPUtil/FPBits.h"
+#include "src/__support/FPUtil/PolyEval.h"
+#include "src/__support/FPUtil/bfloat16.h"
+#include "src/__support/FPUtil/cast.h"
+#include "src/__support/FPUtil/multiply_add.h"
+#include "src/__support/FPUtil/nearest_integer.h"
+#include "src/__support/FPUtil/rounding_mode.h"
+#include "src/__support/macros/attributes.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/macros/optimization.h"
+#include "src/__support/macros/properties/types.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace math {
+
+namespace exp2bf16_internal {
+// Generated by Sollya with the following commands:
+//   > display = hexadecimal;
+//   > for i in [|-126.5, -127, -127.5, -128, -129, -130, -131, -132, -133|]
+//     do print(round(2^i, SG, RN) @ "f,");
+LIBC_INLINE_VAR constexpr float EXP2_DENORMAL[9] = {
+    0x1.6a09e8p-127f, 0x1p-127f, 0x1.6a09e8p-128f, 0x1p-128f, 0x1p-129f,
+    0x1p-130f,        0x1p-131f, 0x1p-132f,        0x1p-133f,
+};
+
+// Generated by Sollya with the following commands:
+//   > display = hexadecimal;
+//   > for i from 0 to 15 do print(round(2^(i/16), SG, RN) @ "f,");
+LIBC_INLINE_VAR constexpr float EXP2_MID[16] = {
+    0x1p0f,        0x1.0b5586p0f, 0x1.172b84p0f, 0x1.2387a6p0f,
+    0x1.306fep0f,  0x1.3dea64p0f, 0x1.4bfdaep0f, 0x1.5ab07ep0f,
+    0x1.6a09e6p0f, 0x1.7a1148p0f, 0x1.8ace54p0f, 0x1.9c4918p0f,
+    0x1.ae89fap0f, 0x1.c199bep0f, 0x1.d5818ep0f, 0x1.ea4afap0f,
+};
+} // namespace exp2bf16_internal
+
+LIBC_INLINE bfloat16 exp2bf16(bfloat16 x) {
+  using FPBits = fputil::FPBits<bfloat16>;
+  FPBits x_bits(x);
+  uint16_t x_uint = x_bits.uintval();
+
+  // We first check for NaN
+  if (LIBC_UNLIKELY(x_bits.is_nan())) {
+    // If it is a signaling NaN, then we raise an FE_INVALID exception
+    // and return a quiet NaN
+    if (x_bits.is_signaling_nan()) {
+      fputil::raise_except_if_required(FE_INVALID);
+      return FPBits::quiet_nan().get_val();
+    }
+    // If not, just return the NaN
+    return x;
+  }
+
+  // exp2bf16 may only give numeric output for input x in (-134.0, 128.0).
+  // All the remaining exceptional cases (overflow, underflow and denormal
+  // outputs) have |x| >= 126.5, so we filter them with a single check.
+  constexpr uint16_t EXCEPTIONAL_ABS_BASE = 0x42FD; // 0x1.fap6 = 126.5
+  if (LIBC_UNLIKELY(x_bits.abs().uintval() >= EXCEPTIONAL_ABS_BASE)) {
+    if (x_bits.is_pos()) {
+      // Overflow: x >= 128.0 (0x4300 = 0x1p7)
+      if (x_uint >= 0x4300) {
+        // exp2(Inf) = Inf
+        if (x_bits.is_inf())
+          return x;
+
+#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+        // If we can only RN, then it'll go towards INF
+        fputil::set_errno_if_required(ERANGE);
+        fputil::raise_overflow_except_if_required<float>();
+        return FPBits::inf().get_val();
+#else
+        // If not, get what other rounding modes are available
+        switch (fputil::quick_get_round()) {
+        case FE_TONEAREST:
+        case FE_UPWARD:
+          // Both of these go to Inf
+          fputil::set_errno_if_required(ERANGE);
+          fputil::raise_overflow_except_if_required<float>();
+          return FPBits::inf().get_val();
+        default:
+          // RD and RZ go to the maximum possible value in the
+          // range of the function, being the maximum normal.
+          return FPBits::max_normal().get_val();
+        }
+#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+      }
+    } else {
+      // Underflow: x <= -134.0 (0xC306 = -0x1.0cp7)
+      if (x_uint >= 0xC306) {
+        // exp2(-Inf) = 0
+        if (x_bits.is_inf())
+          return FPBits::zero().get_val();
+
+        // Need these for every output now.
+        fputil::set_errno_if_required(ERANGE);
+        fputil::raise_underflow_except_if_required<float>();
+#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+        return FPBits::zero().get_val();
+#else
+        switch (fputil::quick_get_round()) {
+        case FE_UPWARD:
+          // RU rounds up to the minimum subnormal
+          return FPBits::min_subnormal().get_val();
+        default:
+          // All other rounding modes go down to zero
+          return FPBits::zero().get_val();
+        }
+#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
+      }
+
+      // Handle denormal output separately, by using a LUT
+      // We are handling these separately because the trick of adding hi
+      // directly to the exponent of the result will not work if the
+      // output is denormal. There are only 9 such inputs and they are
+      // all back-to-back when expressed as an uint16_t, with
+      // x_uint >= 0xC2FD (-0x1.fap6 = -126.5) and
+      // x_uint <= 0xC305 (-0x1.0ap7 = -133.0).
+      constexpr uint16_t DENORMAL_LUT_BASE = 0xC2FD;
+      return fputil::cast<bfloat16>(
+          exp2bf16_internal::EXP2_DENORMAL[x_uint - DENORMAL_LUT_BASE]);
+    }
+  }
+
+  // For inputs not handled above in -134.0 < x < 128.0,
+  // we perform the following range reduction:
+  // x = hi + mid + lo
+  // where,
+  //      hi is an integer
+  //      mid is a grid point of the form j / 2^n in [0, 1)
+  //      lo is what's leftover ie x - k / 2^n
+  //         and |lo| <= 2^(-n-1)
+  // with,
+  //      n = 4
+  //      k = round(2^n * x)
+  //      hi = k >> n
+  //      j = k & (2^n - 1)
+  // then,
+  //      exp2(x) = exp2(hi + mid + lo)
+  //              = exp2(hi) * exp2(mid) * exp2(lo)
+  //
+  // To get exp2(hi), we'll just add hi to the exponent bits of the
+  // result of exp2(mid) * exp2(lo), which is exact.
+  // exp2(mid) we shall obtain by consulting our LUT,
+  // exp2(lo) by the polynomial.
+
+  float x_float = fputil::cast<float>(x);
+  float k_float = fputil::nearest_integer(x_float * 16.0f);
+
+  int k_int = static_cast<int>(k_float);
+  int hi = k_int >> 4;
+  int j = k_int & (16 - 1);
+
+  // lo = x - (hi + mid) = x - k / 2^n
+  //    = k * (-2^(-n)) + x
+  float lo = fputil::multiply_add(k_float, -0.0625f, x_float);
+
+  float exp2_mid = exp2bf16_internal::EXP2_MID[j];
+
+  // Degree-3 minimax polynomial generated by Sollya with the following
+  // commands:
+  //   > display = hexadecimal;
+  //   > P = fpminimax(2^x, [|1, 2, 3|], [|SG...|], [-2^-5, 2^-5], relative,
+  //                   floating, 1);
+  //   0x1p0 + x * (0x1.62e43p-1 + x * (0x1.ebfff4p-3 + x * 0x1.c6aceep-5))
+  float exp2_lo = fputil::polyeval(lo, 0x1p0f, 0x1.62e43p-1f, 0x1.ebfff4p-3f,
+                                   0x1.c6aceep-5f);
+
+  // We tried using Estrin's scheme as follows,
+  //    float lo2 = lo * lo;
+  //    float p0 = fputil::multiply_add(lo, 0x1.62e43p-1f, 0x1p0f);
+  //    float p1 = fputil::multiply_add(lo, 0x1.c6aceep-5f, 0x1.ebfff4p-3f);
+  //    float exp2_lo = fputil::multiply_add(lo2, p1, p0);
+  // however that led to a 1-ULP error in all RU, negative, small input cases.
+  // The latency increase wasn't significant (~1%) so we reverted back
+  // to Horner's scheme.
+
+  float res = exp2_mid * exp2_lo;
+
+  fputil::FPBits<float> res_bits(res);
+  uint32_t res_int = res_bits.uintval();
+
+  uint32_t exp_hi = hi << fputil::FPBits<float>::FRACTION_LEN;
+  res_int += exp_hi;
+
+  res = fputil::FPBits<float>(res_int).get_val();
+  return fputil::cast<bfloat16>(res);
+}
+
+} // namespace math
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_MATH_EXP2BF16_H
diff --git a/libc/src/math/CMakeLists.txt b/libc/src/math/CMakeLists.txt
index 2c7568f0b63618..4f1dd95a6993da 100644
--- a/libc/src/math/CMakeLists.txt
+++ b/libc/src/math/CMakeLists.txt
@@ -154,6 +154,7 @@ add_math_entrypoint_object(expf16)
 add_math_entrypoint_object(expbf16)
 
 add_math_entrypoint_object(exp2)
+add_math_entrypoint_object(exp2bf16)
 add_math_entrypoint_object(exp2f)
 add_math_entrypoint_object(exp2f16)
 
diff --git a/libc/src/math/exp2bf16.h b/libc/src/math/exp2bf16.h
new file mode 100644
index 00000000000000..b19fb2d2f92bc0
--- /dev/null
+++ b/libc/src/math/exp2bf16.h
@@ -0,0 +1,26 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Public declaration of the exp2bf16 entrypoint.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_MATH_EXP2BF16_H
+#define LLVM_LIBC_SRC_MATH_EXP2BF16_H
+
+#include "src/__support/macros/config.h"
+#include "src/__support/macros/properties/types.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+bfloat16 exp2bf16(bfloat16 x);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_MATH_EXP2BF16_H
diff --git a/libc/src/math/generic/CMakeLists.txt b/libc/src/math/generic/CMakeLists.txt
index 28b8e954530ddf..17f586036396f1 100644
--- a/libc/src/math/generic/CMakeLists.txt
+++ b/libc/src/math/generic/CMakeLists.txt
@@ -1284,6 +1284,16 @@ add_entrypoint_object(
     libc.src.__support.math.exp2f
 )
 
+add_entrypoint_object(
+  exp2bf16
+  SRCS
+    exp2bf16.cpp
+  HDRS
+    ../exp2bf16.h
+  DEPENDS
+    libc.src.__support.math.exp2bf16
+)
+
 add_entrypoint_object(
   exp2f16
   SRCS
diff --git a/libc/src/math/generic/exp2bf16.cpp b/libc/src/math/generic/exp2bf16.cpp
new file mode 100644
index 00000000000000..d560c318955df1
--- /dev/null
+++ b/libc/src/math/generic/exp2bf16.cpp
@@ -0,0 +1,23 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Entrypoint definition for the exp2bf16 function.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/__support/math/exp2bf16.h"
+#include "src/math/exp2bf16.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(bfloat16, exp2bf16, (bfloat16 x)) {
+  return math::exp2bf16(x);
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/test/shared/shared_math_test.cpp b/libc/test/shared/shared_math_test.cpp
index 7b785331f36c6a..5b6663fa6494f6 100644
--- a/libc/test/shared/shared_math_test.cpp
+++ b/libc/test/shared/shared_math_test.cpp
@@ -809,6 +809,7 @@ TEST(LlvmLibcSharedMathTest, AllBFloat16) {
                                                          bfloat16(0.0)));
   EXPECT_FP_EQ(bfloat16(0.0), setpayloadsigbf16_res);
 
+  EXPECT_FP_EQ(bfloat16(1.0), LIBC_NAMESPACE::shared::exp2bf16(bfloat16(0.0)));
   EXPECT_FP_EQ(bfloat16(1.0), LIBC_NAMESPACE::shared::expbf16(bfloat16(0.0)));
   EXPECT_FP_EQ(bfloat16(0.0), LIBC_NAMESPACE::shared::log_bf16(bfloat16(1.0)));
 
diff --git a/libc/test/src/math/CMakeLists.txt b/libc/test/src/math/CMakeLists.txt
index 92d599fba947e5..97facaf909097f 100644
--- a/libc/test/src/math/CMakeLists.txt
+++ b/libc/test/src/math/CMakeLists.txt
@@ -1398,6 +1398,18 @@ add_fp_unittest(
     libc.src.__support.FPUtil.fp_bits
 )
 
+add_fp_unittest(
+  exp2bf16_test
+  NEED_MPFR
+  SUITE
+    libc-math-unittests
+  SRCS
+    exp2bf16_test.cpp
+  DEPENDS
+    libc.src.math.exp2bf16
+    libc.src.__support.FPUtil.bfloat16
+)
+
 add_fp_unittest(
   exp2f16_test
   NEED_MPFR
diff --git a/libc/test/src/math/exp2bf16_test.cpp b/libc/test/src/math/exp2bf16_test.cpp
new file mode 100644
index 00000000000000..6b99b8eb62be35
--- /dev/null
+++ b/libc/test/src/math/exp2bf16_test.cpp
@@ -0,0 +1,46 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Exhaustive test for the exp2bf16 function.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/__support/FPUtil/bfloat16.h"
+#include "src/math/exp2bf16.h"
+#include "test/UnitTest/FPMatcher.h"
+#include "test/UnitTest/Test.h"
+#include "utils/MPFRWrapper/MPFRUtils.h"
+
+using LlvmLibcExp2bf16Test = LIBC_NAMESPACE::testing::FPTest<bfloat16>;
+
+namespace mpfr = LIBC_NAMESPACE::testing::mpfr;
+
+// range: [0, inf]
+static constexpr uint16_t POS_START = 0x0000U;
+static constexpr uint16_t POS_STOP = 0x7f80U;
+
+// range: [-0, -inf]
+static constexpr uint16_t NEG_START = 0x8000U;
+static constexpr uint16_t NEG_STOP = 0xff80U;
+
+TEST_F(LlvmLibcExp2bf16Test, PositiveRange) {
+  for (uint16_t v = POS_START; v <= POS_STOP; v++) {
+    bfloat16 x = FPBits(v).get_val();
+    EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Exp2, x,
+                                   LIBC_NAMESPACE::exp2bf16(x), 0.5);
+  }
+}
+
+TEST_F(LlvmLibcExp2bf16Test, NegativeRange) {
+  for (uint16_t v = NEG_START; v <= NEG_STOP; v++) {
+    bfloat16 x = FPBits(v).get_val();
+    EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Exp2, x,
+                                   LIBC_NAMESPACE::exp2bf16(x), 0.5);
+  }
+}
diff --git a/libc/test/src/math/smoke/CMakeLists.txt b/libc/test/src/math/smoke/CMakeLists.txt
index fe9fafdfab9a3f..6f1650fcffa7fd 100644
--- a/libc/test/src/math/smoke/CMakeLists.txt
+++ b/libc/test/src/math/smoke/CMakeLists.txt
@@ -1499,6 +1499,19 @@ add_fp_unittest(
     libc.src.__support.FPUtil.fp_bits
 )
 
+add_fp_unittest(
+  exp2bf16_test
+  SUITE
+    libc-math-smoke-tests
+  SRCS
+    exp2bf16_test.cpp
+  DEPENDS
+    libc.hdr.errno_macros
+    libc.hdr.fenv_macros
+    libc.src.math.exp2bf16
+    libc.src.__support.FPUtil.bfloat16
+)
+
 add_fp_unittest(
   exp2f16_test
   SUITE
diff --git a/libc/test/src/math/smoke/exp2bf16_test.cpp b/libc/test/src/math/smoke/exp2bf16_test.cpp
new file mode 100644
index 00000000000000..c9930fa218b4ce
--- /dev/null
+++ b/libc/test/src/math/smoke/exp2bf16_test.cpp
@@ -0,0 +1,66 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Smoke test for the exp2bf16 function.
+///
+//===----------------------------------------------------------------------===//
+
+#include "hdr/errno_macros.h"
+#include "hdr/fenv_macros.h"
+#include "src/__support/FPUtil/bfloat16.h"
+#include "src/math/exp2bf16.h"
+#include "test/UnitTest/FEnvSafeTest.h"
+#include "test/UnitTest/FPMatcher.h"
+#include "test/UnitTest/Test.h"
+
+class LlvmLibcExp2bf16Test : public LIBC_NAMESPACE::testing::FEnvSafeTest {
+  DECLARE_SPECIAL_CONSTANTS(bfloat16)
+
+public:
+  void test_special_numbers() {
+    EXPECT_FP_EQ_ALL_ROUNDING(aNaN, LIBC_NAMESPACE::exp2bf16(aNaN));
+    EXPECT_MATH_ERRNO(0);
+
+    EXPECT_FP_EQ_WITH_EXCEPTION(aNaN, LIBC_NAMESPACE::exp2bf16(sNaN),
+                                FE_INVALID);
+    EXPECT_MATH_ERRNO(0);
+
+    EXPECT_FP_EQ_ALL_ROUNDING(inf, LIBC_NAMESPACE::exp2bf16(inf));
+    EXPECT_MATH_ERRNO(0);
+
+    EXPECT_FP_EQ_ALL_ROUNDING(zero, LIBC_NAMESPACE::exp2bf16(neg_inf));
+    EXPECT_MATH_ERRNO(0);
+
+    EXPECT_FP_EQ_ALL_ROUNDING(bfloat16(1.0f), LIBC_NAMESPACE::exp2bf16(zero));
+    EXPECT_MATH_ERRNO(0);
+
+    EXPECT_FP_EQ_ALL_ROUNDING(bfloat16(1.0f),
+                              LIBC_NAMESPACE::exp2bf16(neg_zero));
+    EXPECT_MATH_ERRNO(0);
+
+    EXPECT_FP_EQ_WITH_EXCEPTION(inf, LIBC_NAMESPACE::exp2bf16(max_normal),
+                                FE_OVERFLOW);
+    EXPECT_MATH_ERRNO(ERANGE);
+
+    EXPECT_FP_EQ_WITH_EXCEPTION(inf, LIBC_NAMESPACE::exp2bf16(bfloat16(128.0f)),
+                                FE_OVERFLOW);
+    EXPECT_MATH_ERRNO(ERANGE);
+
+    EXPECT_FP_EQ_WITH_EXCEPTION(zero, LIBC_NAMESPACE::exp2bf16(neg_max_normal),
+                                FE_UNDERFLOW | FE_INEXACT);
+    EXPECT_MATH_ERRNO(ERANGE);
+
+    EXPECT_FP_EQ_WITH_EXCEPTION(zero,
+                                LIBC_NAMESPACE::exp2bf16(bfloat16(-134.0f)),
+                                FE_UNDERFLOW | FE_INEXACT);
+    EXPECT_MATH_ERRNO(ERANGE);
+  }
+};
+
+TEST_F(LlvmLibcExp2bf16Test, SpecialNumbers) { test_special_numbers(); }



More information about the libc-commits mailing list