[libc-commits] [libc] [libc][math] Add floating-point cast independent of compiler runtime (PR #105152)

via libc-commits libc-commits at lists.llvm.org
Tue Aug 20 10:41:24 PDT 2024


https://github.com/overmighty updated https://github.com/llvm/llvm-project/pull/105152

>From 1dc49a275b06bc0378f06e1b2645a5f185f1ac71 Mon Sep 17 00:00:00 2001
From: OverMighty <its.overmighty at gmail.com>
Date: Thu, 4 Jul 2024 13:22:16 +0200
Subject: [PATCH 1/2] [libc][math] Add floating-point cast independent of
 compiler runtime

---
 .../cmake/modules/CheckCompilerFeatures.cmake |  19 ++-
 .../check_float16_conversion.cpp              |  30 +++++
 libc/src/__support/FPUtil/CMakeLists.txt      |  25 +++-
 .../__support/FPUtil/ManipulationFunctions.h  |  12 +-
 libc/src/__support/FPUtil/cast.h              |  65 ++++++++++
 libc/src/__support/FPUtil/dyadic_float.h      | 120 +++++++++++++++++-
 .../src/__support/FPUtil/except_value_utils.h |  18 +++
 .../__support/FPUtil/generic/CMakeLists.txt   |   8 +-
 libc/src/__support/FPUtil/generic/FMA.h       |   5 +-
 libc/src/__support/FPUtil/generic/add_sub.h   |   5 +-
 libc/src/__support/FPUtil/generic/sqrt.h      |   3 +-
 libc/src/math/generic/CMakeLists.txt          |  24 +++-
 libc/src/math/generic/ceilf16.cpp             |   3 +-
 libc/src/math/generic/exp10f16.cpp            |  11 +-
 libc/src/math/generic/exp2f16.cpp             |   3 +-
 libc/src/math/generic/expf16.cpp              |   5 +-
 libc/src/math/generic/expm1f16.cpp            |   7 +-
 libc/src/math/generic/floorf16.cpp            |   3 +-
 libc/src/math/generic/rintf16.cpp             |   3 +-
 libc/src/math/generic/roundevenf16.cpp        |   3 +-
 libc/src/math/generic/roundf16.cpp            |   3 +-
 libc/src/math/generic/truncf16.cpp            |   3 +-
 libc/test/src/math/smoke/AddTest.h            |  42 +++---
 libc/test/src/math/smoke/CMakeLists.txt       |  15 +++
 libc/test/src/math/smoke/DivTest.h            |  82 ++++++------
 libc/test/src/math/smoke/FModTest.h           |  64 +++++-----
 libc/test/src/math/smoke/FmaTest.h            |  28 +++-
 libc/test/src/math/smoke/ModfTest.h           |   2 +-
 libc/test/src/math/smoke/MulTest.h            |  52 ++++----
 libc/test/src/math/smoke/NextTowardTest.h     |  14 +-
 libc/test/src/math/smoke/SqrtTest.h           |  16 ++-
 libc/test/src/math/smoke/SubTest.h            |  40 +++---
 libc/test/src/math/smoke/exp10f16_test.cpp    |  14 +-
 libc/test/src/math/smoke/exp2f16_test.cpp     |  14 +-
 libc/test/src/math/smoke/expf16_test.cpp      |  14 +-
 libc/test/src/math/smoke/expm1f16_test.cpp    |  44 ++++---
 libc/utils/MPFRWrapper/CMakeLists.txt         |   1 +
 libc/utils/MPFRWrapper/MPFRUtils.cpp          |   3 +-
 38 files changed, 594 insertions(+), 229 deletions(-)
 create mode 100644 libc/cmake/modules/compiler_features/check_float16_conversion.cpp
 create mode 100644 libc/src/__support/FPUtil/cast.h

diff --git a/libc/cmake/modules/CheckCompilerFeatures.cmake b/libc/cmake/modules/CheckCompilerFeatures.cmake
index 63145fe709dda0..862c7ecbd7fdf4 100644
--- a/libc/cmake/modules/CheckCompilerFeatures.cmake
+++ b/libc/cmake/modules/CheckCompilerFeatures.cmake
@@ -10,6 +10,7 @@ set(
     "builtin_round"
     "builtin_roundeven"
     "float16"
+    "float16_conversion"
     "float128"
     "fixed_point"
 )
@@ -61,15 +62,21 @@ foreach(feature IN LISTS ALL_COMPILER_FEATURES)
   set(link_options "")
   if(${feature} STREQUAL "fixed_point")
     list(APPEND compile_options "-ffixed-point")
-  elseif(${feature} MATCHES "^builtin_")
+  elseif(${feature} MATCHES "^builtin_" OR
+         ${feature} STREQUAL "float16_conversion")
     set(compile_options ${LIBC_COMPILE_OPTIONS_DEFAULT})
     set(link_options -nostdlib)
-    # The compiler might handle calls to rounding builtins by generating calls
-    # to the respective libc math functions, in which case we cannot use these
+    # The compiler might handle calls to math builtins by generating calls to
+    # the respective libc math functions, in which case we cannot use these
     # builtins in our implementations of these functions. We check that this is
     # not the case by trying to link an executable, since linking would fail due
     # to unresolved references with -nostdlib if calls to libc functions were
     # generated.
+    #
+    # We also had issues with soft-float float16 conversion functions using both
+    # compiler-rt and libgcc, so we also check whether we can convert from and
+    # to float16 without calls to compiler runtime functions by trying to link
+    # an executable with -nostdlib.
     set(CMAKE_TRY_COMPILE_TARGET_TYPE EXECUTABLE)
   endif()
 
@@ -97,6 +104,8 @@ foreach(feature IN LISTS ALL_COMPILER_FEATURES)
     list(APPEND AVAILABLE_COMPILER_FEATURES ${feature})
     if(${feature} STREQUAL "float16")
       set(LIBC_TYPES_HAS_FLOAT16 TRUE)
+    elseif(${feature} STREQUAL "float16_conversion")
+      add_compile_definitions(__LIBC_USE_FLOAT16_CONVERSION)
     elseif(${feature} STREQUAL "float128")
       set(LIBC_TYPES_HAS_FLOAT128 TRUE)
     elseif(${feature} STREQUAL "fixed_point")
@@ -115,6 +124,10 @@ foreach(feature IN LISTS ALL_COMPILER_FEATURES)
   endif()
 endforeach()
 
+set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
+set(compile_options ${LIBC_COMPILE_OPTIONS_DEFAULT})
+set(link_options "")
+
 message(STATUS "Compiler features available: ${AVAILABLE_COMPILER_FEATURES}")
 
 ### Compiler Feature Detection ###
diff --git a/libc/cmake/modules/compiler_features/check_float16_conversion.cpp b/libc/cmake/modules/compiler_features/check_float16_conversion.cpp
new file mode 100644
index 00000000000000..09ac8e9c8bc9d6
--- /dev/null
+++ b/libc/cmake/modules/compiler_features/check_float16_conversion.cpp
@@ -0,0 +1,30 @@
+#include "include/llvm-libc-macros/float16-macros.h"
+#include "include/llvm-libc-types/float128.h"
+
+#ifndef LIBC_TYPES_HAS_FLOAT16
+#error unsupported
+#endif
+
+_Float16 cvt_from_float(float x) { return static_cast<_Float16>(x); }
+
+_Float16 cvt_from_double(double x) { return static_cast<_Float16>(x); }
+
+_Float16 cvt_from_long_double(long double x) {
+  return static_cast<_Float16>(x);
+}
+
+#ifdef LIBC_TYPES_HAS_FLOAT128
+_Float16 cvt_from_float128(float128 x) { return static_cast<_Float16>(x); }
+#endif
+
+float cvt_to_float(_Float16 x) { return x; }
+
+double cvt_to_double(_Float16 x) { return x; }
+
+long double cvt_to_long_double(_Float16 x) { return x; }
+
+#ifdef LIBC_TYPES_HAS_FLOAT128
+float128 cvt_to_float128(_Float16 x) { return x; }
+#endif
+
+extern "C" void _start() {}
diff --git a/libc/src/__support/FPUtil/CMakeLists.txt b/libc/src/__support/FPUtil/CMakeLists.txt
index ea1e0e8b39d101..90901549946f42 100644
--- a/libc/src/__support/FPUtil/CMakeLists.txt
+++ b/libc/src/__support/FPUtil/CMakeLists.txt
@@ -97,6 +97,7 @@ add_header_library(
     .rounding_mode
     libc.src.__support.CPP.optional
     libc.src.__support.macros.optimization
+    libc.src.__support.macros.properties.types
 )
 
 
@@ -175,9 +176,13 @@ add_header_library(
     .fenv_impl
     .fp_bits
     .multiply_add
+    .rounding_mode
+    libc.hdr.errno_macros
+    libc.hdr.fenv_macros
     libc.src.__support.CPP.type_traits
     libc.src.__support.big_int
     libc.src.__support.macros.optimization
+    libc.src.__support.macros.properties.types
 )
 
 add_header_library(
@@ -217,18 +222,32 @@ add_header_library(
   HDRS
     ManipulationFunctions.h
   DEPENDS
+    .cast
+    .dyadic_float
     .fenv_impl
     .fp_bits
-    .dyadic_float
     .nearest_integer_operations
     .normal_float
     libc.hdr.math_macros
+    libc.src.errno.errno
+    libc.src.__support.common
     libc.src.__support.CPP.bit
     libc.src.__support.CPP.limits
     libc.src.__support.CPP.type_traits
-    libc.src.__support.common
     libc.src.__support.macros.optimization
-    libc.src.errno.errno
+)
+
+add_header_library(
+  cast
+  HDRS
+    cast.h
+  DEPENDS
+    .dyadic_float
+    .fp_bits
+    libc.hdr.fenv_macros
+    libc.src.__support.CPP.algorithm
+    libc.src.__support.CPP.type_traits
+    libc.src.__support.macros.properties.types
 )
 
 add_subdirectory(generic)
diff --git a/libc/src/__support/FPUtil/ManipulationFunctions.h b/libc/src/__support/FPUtil/ManipulationFunctions.h
index a14f355789999a..66bfe2aa377f99 100644
--- a/libc/src/__support/FPUtil/ManipulationFunctions.h
+++ b/libc/src/__support/FPUtil/ManipulationFunctions.h
@@ -12,6 +12,7 @@
 #include "FPBits.h"
 #include "NearestIntegerOperations.h"
 #include "NormalFloat.h"
+#include "cast.h"
 #include "dyadic_float.h"
 #include "rounding_mode.h"
 
@@ -192,7 +193,8 @@ ldexp(T x, U exp) {
   // For all other values, NormalFloat to T conversion handles it the right way.
   DyadicFloat<FPBits<T>::STORAGE_LEN> normal(bits.get_val());
   normal.exponent += static_cast<int>(exp);
-  return static_cast<T>(normal);
+  // TODO: Add tests for exceptions.
+  return normal.template as<T, /*ShouldRaiseExceptions=*/true>();
 }
 
 template <typename T, typename U,
@@ -207,17 +209,17 @@ LIBC_INLINE T nextafter(T from, U to) {
 
   FPBits<U> to_bits(to);
   if (to_bits.is_nan())
-    return static_cast<T>(to);
+    return cast<T>(to);
 
   // NOTE: This would work only if `U` has a greater or equal precision than
   // `T`. Otherwise `from` could loose its precision and the following statement
   // could incorrectly evaluate to `true`.
-  if (static_cast<U>(from) == to)
-    return static_cast<T>(to);
+  if (cast<U>(from) == to)
+    return cast<T>(to);
 
   using StorageType = typename FPBits<T>::StorageType;
   if (from != T(0)) {
-    if ((static_cast<U>(from) < to) == (from > T(0))) {
+    if ((cast<U>(from) < to) == (from > T(0))) {
       from_bits = FPBits<T>(StorageType(from_bits.uintval() + 1));
     } else {
       from_bits = FPBits<T>(StorageType(from_bits.uintval() - 1));
diff --git a/libc/src/__support/FPUtil/cast.h b/libc/src/__support/FPUtil/cast.h
new file mode 100644
index 00000000000000..126f3852137b77
--- /dev/null
+++ b/libc/src/__support/FPUtil/cast.h
@@ -0,0 +1,65 @@
+//===-- Conversion between floating-point types -----------------*- 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___SUPPORT_FPUTIL_CAST_H
+#define LLVM_LIBC_SRC___SUPPORT_FPUTIL_CAST_H
+
+#include "FPBits.h"
+#include "dyadic_float.h"
+#include "hdr/fenv_macros.h"
+#include "src/__support/CPP/algorithm.h"
+#include "src/__support/CPP/type_traits.h"
+#include "src/__support/macros/properties/types.h"
+
+namespace LIBC_NAMESPACE::fputil {
+
+template <typename OutType, typename InType>
+LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_floating_point_v<OutType> &&
+                                           cpp::is_floating_point_v<InType>,
+                                       OutType>
+cast(InType x) {
+#if defined(LIBC_TYPES_HAS_FLOAT16) && !defined(__LIBC_USE_FLOAT16_CONVERSION)
+  if constexpr (cpp::is_same_v<OutType, float16> ||
+                cpp::is_same_v<InType, float16>) {
+    using InFPBits = FPBits<InType>;
+    using InStorageType = typename InFPBits::StorageType;
+    using OutFPBits = FPBits<OutType>;
+    using OutStorageType = typename OutFPBits::StorageType;
+
+    InFPBits x_bits(x);
+
+    if (x_bits.is_nan()) {
+      if (x_bits.is_signaling_nan()) {
+        raise_except_if_required(FE_INVALID);
+        return OutFPBits::quiet_nan().get_val();
+      }
+
+      InStorageType x_mant = x_bits.get_mantissa();
+      if (InFPBits::FRACTION_LEN > OutFPBits::FRACTION_LEN)
+        x_mant >>= InFPBits::FRACTION_LEN - OutFPBits::FRACTION_LEN;
+      return OutFPBits::quiet_nan(x_bits.sign(),
+                                  static_cast<OutStorageType>(x_mant))
+          .get_val();
+    }
+
+    if (x_bits.is_inf())
+      return OutFPBits::inf(x_bits.sign()).get_val();
+
+    constexpr size_t MAX_FRACTION_LEN =
+        cpp::max(OutFPBits::FRACTION_LEN, InFPBits::FRACTION_LEN);
+    DyadicFloat<cpp::bit_ceil(MAX_FRACTION_LEN)> xd(x);
+    return xd.template as<OutType, /*ShouldSignalExceptions=*/true>();
+  }
+#endif
+
+  return static_cast<OutType>(x);
+}
+
+} // namespace LIBC_NAMESPACE::fputil
+
+#endif // LLVM_LIBC_SRC___SUPPORT_FPUTIL_CAST_H
diff --git a/libc/src/__support/FPUtil/dyadic_float.h b/libc/src/__support/FPUtil/dyadic_float.h
index 86346a47b35a34..814d9423a27f87 100644
--- a/libc/src/__support/FPUtil/dyadic_float.h
+++ b/libc/src/__support/FPUtil/dyadic_float.h
@@ -11,11 +11,15 @@
 
 #include "FEnvImpl.h"
 #include "FPBits.h"
+#include "hdr/errno_macros.h"
+#include "hdr/fenv_macros.h"
 #include "multiply_add.h"
+#include "rounding_mode.h"
 #include "src/__support/CPP/type_traits.h"
 #include "src/__support/big_int.h"
 #include "src/__support/macros/config.h"
 #include "src/__support/macros/optimization.h" // LIBC_UNLIKELY
+#include "src/__support/macros/properties/types.h"
 
 #include <stddef.h>
 
@@ -33,7 +37,8 @@ namespace fputil {
 // The outputs of the constructors and most functions will be normalized.
 // To simplify and improve the efficiency, many functions will assume that the
 // inputs are normal.
-template <size_t Bits> struct DyadicFloat {
+template <size_t Bits> class DyadicFloat {
+public:
   using MantissaType = LIBC_NAMESPACE::UInt<Bits>;
 
   Sign sign = Sign::POS;
@@ -104,6 +109,11 @@ template <size_t Bits> struct DyadicFloat {
                                             (FPBits<T>::FRACTION_LEN < Bits),
                                         void>>
   LIBC_INLINE constexpr T as() const {
+#if defined(LIBC_TYPES_HAS_FLOAT16) && !defined(__LIBC_USE_FLOAT16_CONVERSION)
+    if constexpr (cpp::is_same_v<T, float16>)
+      return generic_as<T>();
+#endif
+
     if (LIBC_UNLIKELY(mantissa.is_zero()))
       return FPBits<T>::zero(sign).get_val();
 
@@ -249,6 +259,114 @@ template <size_t Bits> struct DyadicFloat {
 
     return new_mant;
   }
+
+private:
+  template <typename OutType>
+  LIBC_INLINE constexpr cpp::enable_if_t<
+      cpp::is_floating_point_v<OutType> &&
+          sizeof(typename FPBits<OutType>::StorageType) <= sizeof(MantissaType),
+      OutType>
+  generic_as() const {
+    using FPBits = FPBits<float16>;
+    using StorageType = typename FPBits::StorageType;
+
+    constexpr int EXTRA_FRACTION_LEN = Bits - 1 - FPBits::FRACTION_LEN;
+
+    if (mantissa == 0)
+      return FPBits::zero(sign).get_val();
+
+    int unbiased_exp = get_unbiased_exponent();
+
+    if (unbiased_exp + FPBits::EXP_BIAS >= FPBits::MAX_BIASED_EXPONENT) {
+      set_errno_if_required(ERANGE);
+      raise_except_if_required(FE_OVERFLOW | FE_INEXACT);
+
+      switch (quick_get_round()) {
+      case FE_TONEAREST:
+        return FPBits::inf(sign).get_val();
+      case FE_TOWARDZERO:
+        return FPBits::max_normal(sign).get_val();
+      case FE_DOWNWARD:
+        if (sign.is_pos())
+          return FPBits::max_normal(Sign::POS).get_val();
+        return FPBits::inf(Sign::NEG).get_val();
+      case FE_UPWARD:
+        if (sign.is_neg())
+          return FPBits::max_normal(Sign::NEG).get_val();
+        return FPBits::inf(Sign::POS).get_val();
+      default:
+        __builtin_unreachable();
+      }
+    }
+
+    StorageType out_biased_exp = 0;
+    StorageType out_mantissa = 0;
+    bool round = false;
+    bool sticky = false;
+    bool underflow = false;
+
+    if (unbiased_exp < -FPBits::EXP_BIAS - FPBits::FRACTION_LEN) {
+      sticky = true;
+      underflow = true;
+    } else if (unbiased_exp == -FPBits::EXP_BIAS - FPBits::FRACTION_LEN) {
+      round = true;
+      MantissaType sticky_mask = (MantissaType(1) << (Bits - 1)) - 1;
+      sticky = (mantissa & sticky_mask) != 0;
+    } else {
+      int extra_fraction_len = EXTRA_FRACTION_LEN;
+
+      if (unbiased_exp < 1 - FPBits::EXP_BIAS) {
+        underflow = true;
+        extra_fraction_len += 1 - FPBits::EXP_BIAS - unbiased_exp;
+      } else {
+        out_biased_exp =
+            static_cast<StorageType>(unbiased_exp + FPBits::EXP_BIAS);
+      }
+
+      MantissaType round_mask = MantissaType(1) << (extra_fraction_len - 1);
+      round = (mantissa & round_mask) != 0;
+      MantissaType sticky_mask = round_mask - 1;
+      sticky = (mantissa & sticky_mask) != 0;
+
+      out_mantissa = static_cast<StorageType>(mantissa >> extra_fraction_len);
+    }
+
+    bool lsb = (out_mantissa & 1) != 0;
+
+    StorageType result =
+        FPBits::create_value(sign, out_biased_exp, out_mantissa).uintval();
+
+    switch (quick_get_round()) {
+    case FE_TONEAREST:
+      if (round && (lsb || sticky))
+        ++result;
+      break;
+    case FE_DOWNWARD:
+      if (sign.is_neg() && (round || sticky))
+        ++result;
+      break;
+    case FE_UPWARD:
+      if (sign.is_pos() && (round || sticky))
+        ++result;
+      break;
+    default:
+      break;
+    }
+
+    if (round || sticky) {
+      int excepts = FE_INEXACT;
+      if (FPBits(result).is_inf()) {
+        set_errno_if_required(ERANGE);
+        excepts |= FE_OVERFLOW;
+      } else if (underflow) {
+        set_errno_if_required(ERANGE);
+        excepts |= FE_UNDERFLOW;
+      }
+      raise_except_if_required(excepts);
+    }
+
+    return FPBits(result).get_val();
+  }
 };
 
 // Quick add - Add 2 dyadic floats with rounding toward 0 and then normalize the
diff --git a/libc/src/__support/FPUtil/except_value_utils.h b/libc/src/__support/FPUtil/except_value_utils.h
index b9f54aa24e3a22..3b453fecdec76e 100644
--- a/libc/src/__support/FPUtil/except_value_utils.h
+++ b/libc/src/__support/FPUtil/except_value_utils.h
@@ -13,8 +13,11 @@
 #include "FPBits.h"
 #include "rounding_mode.h"
 #include "src/__support/CPP/optional.h"
+#include "src/__support/FPUtil/cast.h"
 #include "src/__support/macros/config.h"
 #include "src/__support/macros/optimization.h" // LIBC_UNLIKELY
+#include "src/__support/macros/properties/cpu_features.h"
+#include "src/__support/macros/properties/types.h"
 
 namespace LIBC_NAMESPACE_DECL {
 
@@ -113,6 +116,21 @@ template <typename T> LIBC_INLINE T round_result_slightly_up(T value_rn) {
   return tmp;
 }
 
+#if defined(LIBC_TYPES_HAS_FLOAT16) &&                                         \
+    !defined(LIBC_TARGET_CPU_HAS_FAST_FLOAT16_OPS)
+template <> LIBC_INLINE float16 round_result_slightly_down(float16 value_rn) {
+  volatile float tmp = value_rn;
+  tmp -= FPBits<float16>::min_normal().get_val();
+  return cast<float16>(tmp);
+}
+
+template <> LIBC_INLINE float16 round_result_slightly_up(float16 value_rn) {
+  volatile float tmp = value_rn;
+  tmp += FPBits<float16>::min_normal().get_val();
+  return cast<float16>(tmp);
+}
+#endif
+
 } // namespace fputil
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/__support/FPUtil/generic/CMakeLists.txt b/libc/src/__support/FPUtil/generic/CMakeLists.txt
index 43096aa529fc37..b6f58f3fab571a 100644
--- a/libc/src/__support/FPUtil/generic/CMakeLists.txt
+++ b/libc/src/__support/FPUtil/generic/CMakeLists.txt
@@ -8,6 +8,7 @@ add_header_library(
     libc.src.__support.common
     libc.src.__support.CPP.bit
     libc.src.__support.CPP.type_traits
+    libc.src.__support.FPUtil.cast
     libc.src.__support.FPUtil.dyadic_float
     libc.src.__support.FPUtil.fenv_impl
     libc.src.__support.FPUtil.fp_bits
@@ -21,16 +22,17 @@ add_header_library(
     FMA.h
   DEPENDS
     libc.hdr.fenv_macros
+    libc.src.__support.big_int
     libc.src.__support.common
     libc.src.__support.CPP.bit
     libc.src.__support.CPP.limits
     libc.src.__support.CPP.type_traits
     libc.src.__support.FPUtil.basic_operations
+    libc.src.__support.FPUtil.cast
     libc.src.__support.FPUtil.dyadic_float
     libc.src.__support.FPUtil.fenv_impl
     libc.src.__support.FPUtil.fp_bits
     libc.src.__support.FPUtil.rounding_mode
-    libc.src.__support.big_int
     libc.src.__support.macros.optimization
     libc.src.__support.uint128
 )
@@ -60,12 +62,12 @@ add_header_library(
     libc.src.__support.CPP.bit
     libc.src.__support.CPP.type_traits
     libc.src.__support.FPUtil.basic_operations
+    libc.src.__support.FPUtil.cast
+    libc.src.__support.FPUtil.dyadic_float
     libc.src.__support.FPUtil.fenv_impl
     libc.src.__support.FPUtil.fp_bits
-    libc.src.__support.FPUtil.dyadic_float
     libc.src.__support.FPUtil.rounding_mode
     libc.src.__support.macros.attributes
-    libc.src.__support.macros.optimization
 )
 
 add_header_library(
diff --git a/libc/src/__support/FPUtil/generic/FMA.h b/libc/src/__support/FPUtil/generic/FMA.h
index e5683c8ff61ea0..bec312e44b1b10 100644
--- a/libc/src/__support/FPUtil/generic/FMA.h
+++ b/libc/src/__support/FPUtil/generic/FMA.h
@@ -14,6 +14,7 @@
 #include "src/__support/CPP/type_traits.h"
 #include "src/__support/FPUtil/BasicOperations.h"
 #include "src/__support/FPUtil/FPBits.h"
+#include "src/__support/FPUtil/cast.h"
 #include "src/__support/FPUtil/dyadic_float.h"
 #include "src/__support/FPUtil/rounding_mode.h"
 #include "src/__support/big_int.h"
@@ -157,7 +158,7 @@ fma(InType x, InType y, InType z) {
   }
 
   if (LIBC_UNLIKELY(x == 0 || y == 0 || z == 0))
-    return static_cast<OutType>(x * y + z);
+    return cast<OutType>(x * y + z);
 
   int x_exp = 0;
   int y_exp = 0;
@@ -198,7 +199,7 @@ fma(InType x, InType y, InType z) {
   if (LIBC_UNLIKELY(x_exp == InFPBits::MAX_BIASED_EXPONENT ||
                     y_exp == InFPBits::MAX_BIASED_EXPONENT ||
                     z_exp == InFPBits::MAX_BIASED_EXPONENT))
-    return static_cast<OutType>(x * y + z);
+    return cast<OutType>(x * y + z);
 
   // Extract mantissa and append hidden leading bits.
   InStorageType x_mant = x_bits.get_explicit_mantissa();
diff --git a/libc/src/__support/FPUtil/generic/add_sub.h b/libc/src/__support/FPUtil/generic/add_sub.h
index 850db3f83209e6..6bc9dcd23bafad 100644
--- a/libc/src/__support/FPUtil/generic/add_sub.h
+++ b/libc/src/__support/FPUtil/generic/add_sub.h
@@ -17,6 +17,7 @@
 #include "src/__support/FPUtil/BasicOperations.h"
 #include "src/__support/FPUtil/FEnvImpl.h"
 #include "src/__support/FPUtil/FPBits.h"
+#include "src/__support/FPUtil/cast.h"
 #include "src/__support/FPUtil/dyadic_float.h"
 #include "src/__support/FPUtil/rounding_mode.h"
 #include "src/__support/macros/attributes.h"
@@ -106,14 +107,14 @@ add_or_sub(InType x, InType y) {
       volatile InType tmp = y;
       if constexpr (IsSub)
         tmp = -tmp;
-      return static_cast<OutType>(tmp);
+      return cast<OutType>(tmp);
     }
 
     if (y_bits.is_zero()) {
       volatile InType tmp = y;
       if constexpr (IsSub)
         tmp = -tmp;
-      return static_cast<OutType>(tmp);
+      return cast<OutType>(tmp);
     }
   }
 
diff --git a/libc/src/__support/FPUtil/generic/sqrt.h b/libc/src/__support/FPUtil/generic/sqrt.h
index 4502cc07d32b31..01af4bb7c90092 100644
--- a/libc/src/__support/FPUtil/generic/sqrt.h
+++ b/libc/src/__support/FPUtil/generic/sqrt.h
@@ -14,6 +14,7 @@
 #include "src/__support/CPP/type_traits.h"
 #include "src/__support/FPUtil/FEnvImpl.h"
 #include "src/__support/FPUtil/FPBits.h"
+#include "src/__support/FPUtil/cast.h"
 #include "src/__support/FPUtil/dyadic_float.h"
 #include "src/__support/common.h"
 #include "src/__support/macros/config.h"
@@ -96,7 +97,7 @@ sqrt(InType x) {
       // sqrt(-0) = -0
       // sqrt(NaN) = NaN
       // sqrt(-NaN) = -NaN
-      return static_cast<OutType>(x);
+      return cast<OutType>(x);
     } else if (bits.is_neg()) {
       // sqrt(-Inf) = NaN
       // sqrt(-x) = NaN
diff --git a/libc/src/math/generic/CMakeLists.txt b/libc/src/math/generic/CMakeLists.txt
index 350072f4b9649d..01ca65fda9fa18 100644
--- a/libc/src/math/generic/CMakeLists.txt
+++ b/libc/src/math/generic/CMakeLists.txt
@@ -109,9 +109,10 @@ add_entrypoint_object(
   COMPILE_OPTIONS
     -O3
   DEPENDS
-    libc.src.__support.macros.properties.types
+    libc.src.__support.FPUtil.cast
     libc.src.__support.FPUtil.nearest_integer_operations
     libc.src.__support.macros.properties.cpu_features
+    libc.src.__support.macros.properties.types
   FLAGS
     ROUND_OPT
 )
@@ -672,9 +673,10 @@ add_entrypoint_object(
   COMPILE_OPTIONS
     -O3
   DEPENDS
-    libc.src.__support.macros.properties.types
+    libc.src.__support.FPUtil.cast
     libc.src.__support.FPUtil.nearest_integer_operations
     libc.src.__support.macros.properties.cpu_features
+    libc.src.__support.macros.properties.types
   FLAGS
     ROUND_OPT
 )
@@ -741,9 +743,10 @@ add_entrypoint_object(
   COMPILE_OPTIONS
     -O3
   DEPENDS
-    libc.src.__support.macros.properties.types
+    libc.src.__support.FPUtil.cast
     libc.src.__support.FPUtil.nearest_integer_operations
     libc.src.__support.macros.properties.cpu_features
+    libc.src.__support.macros.properties.types
   FLAGS
     ROUND_OPT
 )
@@ -810,9 +813,10 @@ add_entrypoint_object(
   COMPILE_OPTIONS
     -O3
   DEPENDS
-    libc.src.__support.macros.properties.types
+    libc.src.__support.FPUtil.cast
     libc.src.__support.FPUtil.nearest_integer_operations
     libc.src.__support.macros.properties.cpu_features
+    libc.src.__support.macros.properties.types
   FLAGS
     ROUND_OPT
 )
@@ -881,6 +885,7 @@ add_entrypoint_object(
   DEPENDS
     libc.src.__support.macros.properties.types
     libc.src.__support.FPUtil.nearest_integer_operations
+    libc.src.__support.FPUtil.cast
     libc.src.__support.macros.properties.cpu_features
   FLAGS
     ROUND_OPT
@@ -1072,9 +1077,10 @@ add_entrypoint_object(
   COMPILE_OPTIONS
     -O3
   DEPENDS
-    libc.src.__support.macros.properties.types
+    libc.src.__support.FPUtil.cast
     libc.src.__support.FPUtil.nearest_integer_operations
     libc.src.__support.macros.properties.cpu_features
+    libc.src.__support.macros.properties.types
   FLAGS
     ROUND_OPT
 )
@@ -1362,12 +1368,15 @@ add_entrypoint_object(
     .expxf16
     libc.hdr.errno_macros
     libc.hdr.fenv_macros
+    libc.src.__support.CPP.array
+    libc.src.__support.FPUtil.cast
     libc.src.__support.FPUtil.except_value_utils
     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.optimization
   COMPILE_OPTIONS
     -O3
@@ -1442,6 +1451,7 @@ add_entrypoint_object(
     libc.hdr.errno_macros
     libc.hdr.fenv_macros
     libc.src.__support.CPP.array
+    libc.src.__support.FPUtil.cast
     libc.src.__support.FPUtil.except_value_utils
     libc.src.__support.FPUtil.fenv_impl
     libc.src.__support.FPUtil.fp_bits
@@ -1545,6 +1555,7 @@ add_entrypoint_object(
     libc.hdr.errno_macros
     libc.hdr.fenv_macros
     libc.src.__support.CPP.array
+    libc.src.__support.FPUtil.cast
     libc.src.__support.FPUtil.except_value_utils
     libc.src.__support.FPUtil.fenv_impl
     libc.src.__support.FPUtil.fp_bits
@@ -1617,6 +1628,7 @@ add_entrypoint_object(
     .expxf16
     libc.hdr.errno_macros
     libc.hdr.fenv_macros
+    libc.src.__support.FPUtil.cast
     libc.src.__support.FPUtil.except_value_utils
     libc.src.__support.FPUtil.fenv_impl
     libc.src.__support.FPUtil.fp_bits
diff --git a/libc/src/math/generic/ceilf16.cpp b/libc/src/math/generic/ceilf16.cpp
index 8af31c6623a02a..9d89efc5311d18 100644
--- a/libc/src/math/generic/ceilf16.cpp
+++ b/libc/src/math/generic/ceilf16.cpp
@@ -8,6 +8,7 @@
 
 #include "src/math/ceilf16.h"
 #include "src/__support/FPUtil/NearestIntegerOperations.h"
+#include "src/__support/FPUtil/cast.h"
 #include "src/__support/common.h"
 #include "src/__support/macros/config.h"
 #include "src/__support/macros/properties/cpu_features.h"
@@ -17,7 +18,7 @@ namespace LIBC_NAMESPACE_DECL {
 LLVM_LIBC_FUNCTION(float16, ceilf16, (float16 x)) {
 #if defined(__LIBC_USE_BUILTIN_CEIL_FLOOR_RINT_TRUNC) &&                       \
     defined(LIBC_TARGET_CPU_HAS_FAST_FLOAT16_OPS)
-  return static_cast<float16>(__builtin_ceilf(x));
+  return fputil::cast<float16>(__builtin_ceilf(x));
 #else
   return fputil::ceil(x);
 #endif
diff --git a/libc/src/math/generic/exp10f16.cpp b/libc/src/math/generic/exp10f16.cpp
index 9959f7450b591f..1c5966c1f1c126 100644
--- a/libc/src/math/generic/exp10f16.cpp
+++ b/libc/src/math/generic/exp10f16.cpp
@@ -14,6 +14,7 @@
 #include "src/__support/FPUtil/FEnvImpl.h"
 #include "src/__support/FPUtil/FPBits.h"
 #include "src/__support/FPUtil/PolyEval.h"
+#include "src/__support/FPUtil/cast.h"
 #include "src/__support/FPUtil/except_value_utils.h"
 #include "src/__support/FPUtil/multiply_add.h"
 #include "src/__support/FPUtil/nearest_integer.h"
@@ -118,13 +119,13 @@ LLVM_LIBC_FUNCTION(float16, exp10f16, (float16 x)) {
   if (LIBC_UNLIKELY((x_u & ~(0x3c00U | 0x4000U | 0x4200U | 0x4400U)) == 0)) {
     switch (x_u) {
     case 0x3c00U: // x = 1.0f16
-      return static_cast<float16>(10.0);
+      return fputil::cast<float16>(10.0);
     case 0x4000U: // x = 2.0f16
-      return static_cast<float16>(100.0);
+      return fputil::cast<float16>(100.0);
     case 0x4200U: // x = 3.0f16
-      return static_cast<float16>(1'000.0);
+      return fputil::cast<float16>(1'000.0);
     case 0x4400U: // x = 4.0f16
-      return static_cast<float16>(10'000.0);
+      return fputil::cast<float16>(10'000.0);
     }
   }
 
@@ -164,7 +165,7 @@ LLVM_LIBC_FUNCTION(float16, exp10f16, (float16 x)) {
   //   > 1 + x * P;
   float exp10_lo = fputil::polyeval(lo, 0x1p+0f, 0x1.26bb14p+1f, 0x1.53526p+1f,
                                     0x1.04b434p+1f, 0x1.2bcf9ep+0f);
-  return static_cast<float16>(exp2_hi_mid * exp10_lo);
+  return fputil::cast<float16>(exp2_hi_mid * exp10_lo);
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/math/generic/exp2f16.cpp b/libc/src/math/generic/exp2f16.cpp
index 66b79567040053..3c4310259b1df9 100644
--- a/libc/src/math/generic/exp2f16.cpp
+++ b/libc/src/math/generic/exp2f16.cpp
@@ -14,6 +14,7 @@
 #include "src/__support/FPUtil/FEnvImpl.h"
 #include "src/__support/FPUtil/FPBits.h"
 #include "src/__support/FPUtil/PolyEval.h"
+#include "src/__support/FPUtil/cast.h"
 #include "src/__support/FPUtil/except_value_utils.h"
 #include "src/__support/FPUtil/multiply_add.h"
 #include "src/__support/FPUtil/nearest_integer.h"
@@ -121,7 +122,7 @@ LLVM_LIBC_FUNCTION(float16, exp2f16, (float16 x)) {
   //   > 1 + x * P;
   float exp2_lo = fputil::polyeval(lo, 0x1p+0f, 0x1.62e43p-1f, 0x1.ec0aa6p-3f,
                                    0x1.c6b4a6p-5f);
-  return static_cast<float16>(exp2_hi_mid * exp2_lo);
+  return fputil::cast<float16>(exp2_hi_mid * exp2_lo);
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/math/generic/expf16.cpp b/libc/src/math/generic/expf16.cpp
index 7ffdbd5191008a..0548ef3932ae92 100644
--- a/libc/src/math/generic/expf16.cpp
+++ b/libc/src/math/generic/expf16.cpp
@@ -13,6 +13,7 @@
 #include "src/__support/FPUtil/FEnvImpl.h"
 #include "src/__support/FPUtil/FPBits.h"
 #include "src/__support/FPUtil/PolyEval.h"
+#include "src/__support/FPUtil/cast.h"
 #include "src/__support/FPUtil/except_value_utils.h"
 #include "src/__support/FPUtil/rounding_mode.h"
 #include "src/__support/common.h"
@@ -103,7 +104,7 @@ LLVM_LIBC_FUNCTION(float16, expf16, (float16 x)) {
       //   > display = hexadecimal;
       //   > P = fpminimax(expm1(x)/x, 2, [|SG...|], [-2^-5, 2^-5]);
       //   > 1 + x * P;
-      return static_cast<float16>(
+      return fputil::cast<float16>(
           fputil::polyeval(xf, 0x1p+0f, 0x1p+0f, 0x1.0004p-1f, 0x1.555778p-3f));
     }
   }
@@ -113,7 +114,7 @@ LLVM_LIBC_FUNCTION(float16, expf16, (float16 x)) {
 
   // exp(x) = exp(hi + mid) * exp(lo)
   auto [exp_hi_mid, exp_lo] = exp_range_reduction(x);
-  return static_cast<float16>(exp_hi_mid * exp_lo);
+  return fputil::cast<float16>(exp_hi_mid * exp_lo);
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/math/generic/expm1f16.cpp b/libc/src/math/generic/expm1f16.cpp
index 0facdc510e4287..4ce0efd1f461bb 100644
--- a/libc/src/math/generic/expm1f16.cpp
+++ b/libc/src/math/generic/expm1f16.cpp
@@ -13,6 +13,7 @@
 #include "src/__support/FPUtil/FEnvImpl.h"
 #include "src/__support/FPUtil/FPBits.h"
 #include "src/__support/FPUtil/PolyEval.h"
+#include "src/__support/FPUtil/cast.h"
 #include "src/__support/FPUtil/except_value_utils.h"
 #include "src/__support/FPUtil/multiply_add.h"
 #include "src/__support/FPUtil/rounding_mode.h"
@@ -99,7 +100,7 @@ LLVM_LIBC_FUNCTION(float16, expm1f16, (float16 x)) {
             FPBits::one(Sign::NEG).get_val());
       // When x <= -0x1.0ap+3, round(expm1(x), HP, RN) = -0x1.ffcp-1.
       return fputil::round_result_slightly_down(
-          static_cast<float16>(-0x1.ffcp-1));
+          fputil::cast<float16>(-0x1.ffcp-1));
     }
 
     // When 0 < |x| <= 2^(-3).
@@ -114,7 +115,7 @@ LLVM_LIBC_FUNCTION(float16, expm1f16, (float16 x)) {
       //   > display = hexadecimal;
       //   > P = fpminimax(expm1(x)/x, 4, [|SG...|], [-2^-3, 2^-3]);
       //   > x * P;
-      return static_cast<float16>(
+      return fputil::cast<float16>(
           xf * fputil::polyeval(xf, 0x1p+0f, 0x1.fffff8p-2f, 0x1.555556p-3f,
                                 0x1.55905ep-5f, 0x1.1124c2p-7f));
     }
@@ -126,7 +127,7 @@ LLVM_LIBC_FUNCTION(float16, expm1f16, (float16 x)) {
   // exp(x) = exp(hi + mid) * exp(lo)
   auto [exp_hi_mid, exp_lo] = exp_range_reduction(x);
   // expm1(x) = exp(hi + mid) * exp(lo) - 1
-  return static_cast<float16>(fputil::multiply_add(exp_hi_mid, exp_lo, -1.0f));
+  return fputil::cast<float16>(fputil::multiply_add(exp_hi_mid, exp_lo, -1.0f));
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/math/generic/floorf16.cpp b/libc/src/math/generic/floorf16.cpp
index 3092048f5ab061..361b22729f642e 100644
--- a/libc/src/math/generic/floorf16.cpp
+++ b/libc/src/math/generic/floorf16.cpp
@@ -8,6 +8,7 @@
 
 #include "src/math/floorf16.h"
 #include "src/__support/FPUtil/NearestIntegerOperations.h"
+#include "src/__support/FPUtil/cast.h"
 #include "src/__support/common.h"
 #include "src/__support/macros/config.h"
 #include "src/__support/macros/properties/cpu_features.h"
@@ -17,7 +18,7 @@ namespace LIBC_NAMESPACE_DECL {
 LLVM_LIBC_FUNCTION(float16, floorf16, (float16 x)) {
 #if defined(__LIBC_USE_BUILTIN_CEIL_FLOOR_RINT_TRUNC) &&                       \
     defined(LIBC_TARGET_CPU_HAS_FAST_FLOAT16_OPS)
-  return static_cast<float16>(__builtin_floorf(x));
+  return fputil::cast<float16>(__builtin_floorf(x));
 #else
   return fputil::floor(x);
 #endif
diff --git a/libc/src/math/generic/rintf16.cpp b/libc/src/math/generic/rintf16.cpp
index 3a53dd28e3d109..aefdcbea770644 100644
--- a/libc/src/math/generic/rintf16.cpp
+++ b/libc/src/math/generic/rintf16.cpp
@@ -8,6 +8,7 @@
 
 #include "src/math/rintf16.h"
 #include "src/__support/FPUtil/NearestIntegerOperations.h"
+#include "src/__support/FPUtil/cast.h"
 #include "src/__support/common.h"
 #include "src/__support/macros/config.h"
 #include "src/__support/macros/properties/cpu_features.h"
@@ -17,7 +18,7 @@ namespace LIBC_NAMESPACE_DECL {
 LLVM_LIBC_FUNCTION(float16, rintf16, (float16 x)) {
 #if defined(__LIBC_USE_BUILTIN_CEIL_FLOOR_RINT_TRUNC) &&                       \
     defined(LIBC_TARGET_CPU_HAS_FAST_FLOAT16_OPS)
-  return static_cast<float16>(__builtin_rintf(x));
+  return fputil::cast<float16>(__builtin_rintf(x));
 #else
   return fputil::round_using_current_rounding_mode(x);
 #endif
diff --git a/libc/src/math/generic/roundevenf16.cpp b/libc/src/math/generic/roundevenf16.cpp
index c3dbd779b97395..fdcd968bc9b874 100644
--- a/libc/src/math/generic/roundevenf16.cpp
+++ b/libc/src/math/generic/roundevenf16.cpp
@@ -8,6 +8,7 @@
 
 #include "src/math/roundevenf16.h"
 #include "src/__support/FPUtil/NearestIntegerOperations.h"
+#include "src/__support/FPUtil/cast.h"
 #include "src/__support/common.h"
 #include "src/__support/macros/config.h"
 #include "src/__support/macros/properties/cpu_features.h"
@@ -17,7 +18,7 @@ namespace LIBC_NAMESPACE_DECL {
 LLVM_LIBC_FUNCTION(float16, roundevenf16, (float16 x)) {
 #if defined(__LIBC_USE_BUILTIN_ROUNDEVEN) &&                                   \
     defined(LIBC_TARGET_CPU_HAS_FAST_FLOAT16_OPS)
-  return static_cast<float16>(__builtin_roundevenf(x));
+  return fputil::cast<float16>(__builtin_roundevenf(x));
 #else
   return fputil::round_using_specific_rounding_mode(x, FP_INT_TONEAREST);
 #endif
diff --git a/libc/src/math/generic/roundf16.cpp b/libc/src/math/generic/roundf16.cpp
index a5e2b44fbd54bd..9adfb52ed27c67 100644
--- a/libc/src/math/generic/roundf16.cpp
+++ b/libc/src/math/generic/roundf16.cpp
@@ -8,6 +8,7 @@
 
 #include "src/math/roundf16.h"
 #include "src/__support/FPUtil/NearestIntegerOperations.h"
+#include "src/__support/FPUtil/cast.h"
 #include "src/__support/common.h"
 #include "src/__support/macros/config.h"
 #include "src/__support/macros/properties/cpu_features.h"
@@ -17,7 +18,7 @@ namespace LIBC_NAMESPACE_DECL {
 LLVM_LIBC_FUNCTION(float16, roundf16, (float16 x)) {
 #if defined(__LIBC_USE_BUILTIN_ROUND) &&                                       \
     defined(LIBC_TARGET_CPU_HAS_FAST_FLOAT16_OPS)
-  return static_cast<float16>(__builtin_roundf(x));
+  return fputil::cast<float16>(__builtin_roundf(x));
 #else
   return fputil::round(x);
 #endif
diff --git a/libc/src/math/generic/truncf16.cpp b/libc/src/math/generic/truncf16.cpp
index 31b1214a9a0e4b..4d37e6560a965b 100644
--- a/libc/src/math/generic/truncf16.cpp
+++ b/libc/src/math/generic/truncf16.cpp
@@ -8,6 +8,7 @@
 
 #include "src/math/truncf16.h"
 #include "src/__support/FPUtil/NearestIntegerOperations.h"
+#include "src/__support/FPUtil/cast.h"
 #include "src/__support/common.h"
 #include "src/__support/macros/config.h"
 #include "src/__support/macros/properties/cpu_features.h"
@@ -17,7 +18,7 @@ namespace LIBC_NAMESPACE_DECL {
 LLVM_LIBC_FUNCTION(float16, truncf16, (float16 x)) {
 #if defined(__LIBC_USE_BUILTIN_CEIL_FLOOR_RINT_TRUNC) &&                       \
     defined(LIBC_TARGET_CPU_HAS_FAST_FLOAT16_OPS)
-  return static_cast<float16>(__builtin_truncf(x));
+  return fputil::cast<float16>(__builtin_truncf(x));
 #else
   return fputil::trunc(x);
 #endif
diff --git a/libc/test/src/math/smoke/AddTest.h b/libc/test/src/math/smoke/AddTest.h
index 0b7e395a22d4cd..4474845055fe9a 100644
--- a/libc/test/src/math/smoke/AddTest.h
+++ b/libc/test/src/math/smoke/AddTest.h
@@ -34,32 +34,33 @@ class AddTest : public LIBC_NAMESPACE::testing::FEnvSafeTest {
   using AddFunc = OutType (*)(InType, InType);
 
   void test_special_numbers(AddFunc func) {
-    EXPECT_FP_IS_NAN(func(aNaN, aNaN));
-    EXPECT_FP_IS_NAN_WITH_EXCEPTION(func(sNaN, sNaN), FE_INVALID);
+    EXPECT_FP_IS_NAN(func(in.aNaN, in.aNaN));
+    EXPECT_FP_IS_NAN_WITH_EXCEPTION(func(in.sNaN, in.sNaN), FE_INVALID);
 
     InType qnan_42 = InFPBits::quiet_nan(Sign::POS, 0x42).get_val();
-    EXPECT_FP_IS_NAN(func(qnan_42, zero));
-    EXPECT_FP_IS_NAN(func(zero, qnan_42));
+    EXPECT_FP_IS_NAN(func(qnan_42, in.zero));
+    EXPECT_FP_IS_NAN(func(in.zero, qnan_42));
 
-    EXPECT_FP_EQ(inf, func(inf, zero));
-    EXPECT_FP_EQ(neg_inf, func(neg_inf, zero));
-    EXPECT_FP_EQ(inf, func(inf, neg_zero));
-    EXPECT_FP_EQ(neg_inf, func(neg_inf, neg_zero));
+    EXPECT_FP_EQ(inf, func(in.inf, in.zero));
+    EXPECT_FP_EQ(neg_inf, func(in.neg_inf, in.zero));
+    EXPECT_FP_EQ(inf, func(in.inf, in.neg_zero));
+    EXPECT_FP_EQ(neg_inf, func(in.neg_inf, in.neg_zero));
   }
 
   void test_invalid_operations(AddFunc func) {
-    EXPECT_FP_IS_NAN_WITH_EXCEPTION(func(inf, neg_inf), FE_INVALID);
-    EXPECT_FP_IS_NAN_WITH_EXCEPTION(func(neg_inf, inf), FE_INVALID);
+    EXPECT_FP_IS_NAN_WITH_EXCEPTION(func(in.inf, in.neg_inf), FE_INVALID);
+    EXPECT_FP_IS_NAN_WITH_EXCEPTION(func(in.neg_inf, in.inf), FE_INVALID);
   }
 
   void test_range_errors(AddFunc func) {
     using namespace LIBC_NAMESPACE::fputil::testing;
 
     if (ForceRoundingMode r(RoundingMode::Nearest); r.success) {
-      EXPECT_FP_EQ_WITH_EXCEPTION(inf, func(max_normal, max_normal),
+      EXPECT_FP_EQ_WITH_EXCEPTION(inf, func(in.max_normal, in.max_normal),
                                   FE_OVERFLOW | FE_INEXACT);
       EXPECT_MATH_ERRNO(ERANGE);
-      EXPECT_FP_EQ_WITH_EXCEPTION(-inf, func(neg_max_normal, neg_max_normal),
+      EXPECT_FP_EQ_WITH_EXCEPTION(-inf,
+                                  func(in.neg_max_normal, in.neg_max_normal),
                                   FE_OVERFLOW | FE_INEXACT);
       EXPECT_MATH_ERRNO(ERANGE);
 
@@ -73,10 +74,11 @@ class AddTest : public LIBC_NAMESPACE::testing::FEnvSafeTest {
     }
 
     if (ForceRoundingMode r(RoundingMode::TowardZero); r.success) {
-      EXPECT_FP_EQ_WITH_EXCEPTION(max_normal, func(max_normal, max_normal),
+      EXPECT_FP_EQ_WITH_EXCEPTION(max_normal,
+                                  func(in.max_normal, in.max_normal),
                                   FE_OVERFLOW | FE_INEXACT);
       EXPECT_FP_EQ_WITH_EXCEPTION(neg_max_normal,
-                                  func(neg_max_normal, neg_max_normal),
+                                  func(in.neg_max_normal, in.neg_max_normal),
                                   FE_OVERFLOW | FE_INEXACT);
 
       EXPECT_FP_EQ_WITH_EXCEPTION(zero, func(in.min_denormal, in.min_denormal),
@@ -89,9 +91,11 @@ class AddTest : public LIBC_NAMESPACE::testing::FEnvSafeTest {
     }
 
     if (ForceRoundingMode r(RoundingMode::Downward); r.success) {
-      EXPECT_FP_EQ_WITH_EXCEPTION(max_normal, func(max_normal, max_normal),
+      EXPECT_FP_EQ_WITH_EXCEPTION(max_normal,
+                                  func(in.max_normal, in.max_normal),
                                   FE_OVERFLOW | FE_INEXACT);
-      EXPECT_FP_EQ_WITH_EXCEPTION(-inf, func(neg_max_normal, neg_max_normal),
+      EXPECT_FP_EQ_WITH_EXCEPTION(-inf,
+                                  func(in.neg_max_normal, in.neg_max_normal),
                                   FE_OVERFLOW | FE_INEXACT);
       EXPECT_MATH_ERRNO(ERANGE);
 
@@ -105,11 +109,11 @@ class AddTest : public LIBC_NAMESPACE::testing::FEnvSafeTest {
     }
 
     if (ForceRoundingMode r(RoundingMode::Upward); r.success) {
-      EXPECT_FP_EQ_WITH_EXCEPTION(inf, func(max_normal, max_normal),
+      EXPECT_FP_EQ_WITH_EXCEPTION(inf, func(in.max_normal, in.max_normal),
                                   FE_OVERFLOW | FE_INEXACT);
       EXPECT_MATH_ERRNO(ERANGE);
       EXPECT_FP_EQ_WITH_EXCEPTION(neg_max_normal,
-                                  func(neg_max_normal, neg_max_normal),
+                                  func(in.neg_max_normal, in.neg_max_normal),
                                   FE_OVERFLOW | FE_INEXACT);
 
       EXPECT_FP_EQ_WITH_EXCEPTION(min_denormal,
@@ -124,7 +128,7 @@ class AddTest : public LIBC_NAMESPACE::testing::FEnvSafeTest {
   }
 
   void test_inexact_results(AddFunc func) {
-    func(InType(1.0), min_denormal);
+    func(InType(1.0), in.min_denormal);
     EXPECT_FP_EXCEPTION(FE_INEXACT);
   }
 };
diff --git a/libc/test/src/math/smoke/CMakeLists.txt b/libc/test/src/math/smoke/CMakeLists.txt
index 7271e933b9311d..1ded60b78597ae 100644
--- a/libc/test/src/math/smoke/CMakeLists.txt
+++ b/libc/test/src/math/smoke/CMakeLists.txt
@@ -398,6 +398,7 @@ add_fp_unittest(
     FmaTest.h
   DEPENDS
     libc.src.math.dfmal
+    libc.src.__support.macros.properties.types
 )
 
 add_fp_unittest(
@@ -410,6 +411,7 @@ add_fp_unittest(
     FmaTest.h
   DEPENDS
     libc.src.math.dfmaf128
+    libc.src.__support.macros.properties.types
 )
 
 add_fp_unittest(
@@ -1057,6 +1059,7 @@ add_fp_unittest(
     libc.hdr.fenv_macros
     libc.src.errno.errno
     libc.src.math.expf16
+    libc.src.__support.FPUtil.cast
 )
 
 add_fp_unittest(
@@ -1093,6 +1096,7 @@ add_fp_unittest(
     libc.hdr.fenv_macros
     libc.src.errno.errno
     libc.src.math.exp2f16
+    libc.src.__support.FPUtil.cast
 )
 
 add_fp_unittest(
@@ -1140,6 +1144,7 @@ add_fp_unittest(
     libc.hdr.fenv_macros
     libc.src.errno.errno
     libc.src.math.exp10f16
+    libc.src.__support.FPUtil.cast
 )
 
 add_fp_unittest(
@@ -3308,6 +3313,7 @@ add_fp_unittest(
     FmaTest.h
   DEPENDS
     libc.src.math.fmaf
+    libc.src.__support.macros.properties.types
   FLAGS
     FMA_OPT__ONLY
 )
@@ -3322,6 +3328,7 @@ add_fp_unittest(
     FmaTest.h
   DEPENDS
     libc.src.math.fma
+    libc.src.__support.macros.properties.types
 )
 
 add_fp_unittest(
@@ -3359,6 +3366,7 @@ add_fp_unittest(
     libc.hdr.fenv_macros
     libc.src.errno.errno
     libc.src.math.expm1f16
+    libc.src.__support.FPUtil.cast
 )
 
 add_fp_unittest(
@@ -4335,6 +4343,7 @@ add_fp_unittest(
     FmaTest.h
   DEPENDS
     libc.src.math.f16fma
+    libc.src.__support.macros.properties.types
 )
 
 add_fp_unittest(
@@ -4347,6 +4356,7 @@ add_fp_unittest(
     FmaTest.h
   DEPENDS
     libc.src.math.f16fmaf
+    libc.src.__support.macros.properties.types
 )
 
 add_fp_unittest(
@@ -4359,6 +4369,7 @@ add_fp_unittest(
     FmaTest.h
   DEPENDS
     libc.src.math.f16fmal
+    libc.src.__support.macros.properties.types
 )
 
 add_fp_unittest(
@@ -4371,6 +4382,7 @@ add_fp_unittest(
     FmaTest.h
   DEPENDS
     libc.src.math.f16fmaf128
+    libc.src.__support.macros.properties.types
 )
 
 add_fp_unittest(
@@ -4473,6 +4485,7 @@ add_fp_unittest(
     FmaTest.h
   DEPENDS
     libc.src.math.ffma
+    libc.src.__support.macros.properties.types
 )
 
 add_fp_unittest(
@@ -4485,6 +4498,7 @@ add_fp_unittest(
     FmaTest.h
   DEPENDS
     libc.src.math.ffmal
+    libc.src.__support.macros.properties.types
 )
 
 add_fp_unittest(
@@ -4497,6 +4511,7 @@ add_fp_unittest(
     FmaTest.h
   DEPENDS
     libc.src.math.ffmaf128
+    libc.src.__support.macros.properties.types
 )
 
 add_fp_unittest(
diff --git a/libc/test/src/math/smoke/DivTest.h b/libc/test/src/math/smoke/DivTest.h
index 666179628c55ff..60e7a8adc9eba3 100644
--- a/libc/test/src/math/smoke/DivTest.h
+++ b/libc/test/src/math/smoke/DivTest.h
@@ -28,45 +28,47 @@ class DivTest : public LIBC_NAMESPACE::testing::FEnvSafeTest {
   using InFPBits = typename InConstants::FPBits;
   using InStorageType = typename InConstants::StorageType;
 
+  InConstants in;
+
 public:
   using DivFunc = OutType (*)(InType, InType);
 
   void test_special_numbers(DivFunc func) {
-    EXPECT_FP_IS_NAN(func(aNaN, aNaN));
-    EXPECT_FP_IS_NAN_WITH_EXCEPTION(func(sNaN, sNaN), FE_INVALID);
+    EXPECT_FP_IS_NAN(func(in.aNaN, in.aNaN));
+    EXPECT_FP_IS_NAN_WITH_EXCEPTION(func(in.sNaN, in.sNaN), FE_INVALID);
 
     InType qnan_42 = InFPBits::quiet_nan(Sign::POS, 0x42).get_val();
-    EXPECT_FP_IS_NAN(func(qnan_42, zero));
-    EXPECT_FP_IS_NAN(func(zero, qnan_42));
+    EXPECT_FP_IS_NAN(func(qnan_42, in.zero));
+    EXPECT_FP_IS_NAN(func(in.zero, qnan_42));
 
-    EXPECT_FP_EQ(inf, func(inf, zero));
-    EXPECT_FP_EQ(neg_inf, func(neg_inf, zero));
-    EXPECT_FP_EQ(neg_inf, func(inf, neg_zero));
-    EXPECT_FP_EQ(inf, func(neg_inf, neg_zero));
+    EXPECT_FP_EQ(inf, func(in.inf, in.zero));
+    EXPECT_FP_EQ(neg_inf, func(in.neg_inf, in.zero));
+    EXPECT_FP_EQ(neg_inf, func(in.inf, in.neg_zero));
+    EXPECT_FP_EQ(inf, func(in.neg_inf, in.neg_zero));
   }
 
   void test_division_by_zero(DivFunc func) {
-    EXPECT_FP_EQ_WITH_EXCEPTION(inf, func(InType(1.0), zero), FE_DIVBYZERO);
-    EXPECT_FP_EQ_WITH_EXCEPTION(neg_inf, func(InType(-1.0), zero),
+    EXPECT_FP_EQ_WITH_EXCEPTION(inf, func(InType(1.0), in.zero), FE_DIVBYZERO);
+    EXPECT_FP_EQ_WITH_EXCEPTION(neg_inf, func(InType(-1.0), in.zero),
                                 FE_DIVBYZERO);
-    EXPECT_FP_EQ_WITH_EXCEPTION(neg_inf, func(InType(1.0), neg_zero),
+    EXPECT_FP_EQ_WITH_EXCEPTION(neg_inf, func(InType(1.0), in.neg_zero),
                                 FE_DIVBYZERO);
-    EXPECT_FP_EQ_WITH_EXCEPTION(inf, func(InType(1.0), zero), FE_DIVBYZERO);
+    EXPECT_FP_EQ_WITH_EXCEPTION(inf, func(InType(1.0), in.zero), FE_DIVBYZERO);
   }
 
   void test_invalid_operations(DivFunc func) {
-    EXPECT_FP_IS_NAN_WITH_EXCEPTION(func(zero, zero), FE_INVALID);
-    EXPECT_FP_IS_NAN_WITH_EXCEPTION(func(neg_zero, zero), FE_INVALID);
-    EXPECT_FP_IS_NAN_WITH_EXCEPTION(func(zero, neg_zero), FE_INVALID);
-    EXPECT_FP_IS_NAN_WITH_EXCEPTION(func(neg_zero, neg_zero), FE_INVALID);
+    EXPECT_FP_IS_NAN_WITH_EXCEPTION(func(in.zero, in.zero), FE_INVALID);
+    EXPECT_FP_IS_NAN_WITH_EXCEPTION(func(in.neg_zero, in.zero), FE_INVALID);
+    EXPECT_FP_IS_NAN_WITH_EXCEPTION(func(in.zero, in.neg_zero), FE_INVALID);
+    EXPECT_FP_IS_NAN_WITH_EXCEPTION(func(in.neg_zero, in.neg_zero), FE_INVALID);
 
-    EXPECT_FP_IS_NAN_WITH_EXCEPTION(func(inf, inf), FE_INVALID);
+    EXPECT_FP_IS_NAN_WITH_EXCEPTION(func(in.inf, in.inf), FE_INVALID);
     EXPECT_MATH_ERRNO(EDOM);
-    EXPECT_FP_IS_NAN_WITH_EXCEPTION(func(neg_inf, inf), FE_INVALID);
+    EXPECT_FP_IS_NAN_WITH_EXCEPTION(func(in.neg_inf, in.inf), FE_INVALID);
     EXPECT_MATH_ERRNO(EDOM);
-    EXPECT_FP_IS_NAN_WITH_EXCEPTION(func(inf, neg_inf), FE_INVALID);
+    EXPECT_FP_IS_NAN_WITH_EXCEPTION(func(in.inf, in.neg_inf), FE_INVALID);
     EXPECT_MATH_ERRNO(EDOM);
-    EXPECT_FP_IS_NAN_WITH_EXCEPTION(func(neg_inf, neg_inf), FE_INVALID);
+    EXPECT_FP_IS_NAN_WITH_EXCEPTION(func(in.neg_inf, in.neg_inf), FE_INVALID);
     EXPECT_MATH_ERRNO(EDOM);
   }
 
@@ -74,64 +76,72 @@ class DivTest : public LIBC_NAMESPACE::testing::FEnvSafeTest {
     using namespace LIBC_NAMESPACE::fputil::testing;
 
     if (ForceRoundingMode r(RoundingMode::Nearest); r.success) {
-      EXPECT_FP_EQ_WITH_EXCEPTION(inf, func(max_normal, min_normal),
+      EXPECT_FP_EQ_WITH_EXCEPTION(inf, func(in.max_normal, in.min_normal),
                                   FE_OVERFLOW | FE_INEXACT);
       EXPECT_MATH_ERRNO(ERANGE);
-      EXPECT_FP_EQ_WITH_EXCEPTION(-inf, func(neg_max_normal, min_denormal),
+      EXPECT_FP_EQ_WITH_EXCEPTION(-inf,
+                                  func(in.neg_max_normal, in.min_denormal),
                                   FE_OVERFLOW | FE_INEXACT);
       EXPECT_MATH_ERRNO(ERANGE);
 
-      EXPECT_FP_EQ_WITH_EXCEPTION(zero, func(min_denormal, max_normal),
+      EXPECT_FP_EQ_WITH_EXCEPTION(zero, func(in.min_denormal, in.max_normal),
                                   FE_UNDERFLOW | FE_INEXACT);
       EXPECT_MATH_ERRNO(ERANGE);
-      EXPECT_FP_EQ_WITH_EXCEPTION(neg_zero, func(neg_min_denormal, max_normal),
+      EXPECT_FP_EQ_WITH_EXCEPTION(neg_zero,
+                                  func(in.neg_min_denormal, in.max_normal),
                                   FE_UNDERFLOW | FE_INEXACT);
       EXPECT_MATH_ERRNO(ERANGE);
     }
 
     if (ForceRoundingMode r(RoundingMode::TowardZero); r.success) {
-      EXPECT_FP_EQ_WITH_EXCEPTION(max_normal, func(max_normal, min_normal),
+      EXPECT_FP_EQ_WITH_EXCEPTION(max_normal,
+                                  func(in.max_normal, in.min_normal),
                                   FE_OVERFLOW | FE_INEXACT);
       EXPECT_FP_EQ_WITH_EXCEPTION(neg_max_normal,
-                                  func(neg_max_normal, min_denormal),
+                                  func(in.neg_max_normal, in.min_denormal),
                                   FE_OVERFLOW | FE_INEXACT);
 
-      EXPECT_FP_EQ_WITH_EXCEPTION(zero, func(min_denormal, max_normal),
+      EXPECT_FP_EQ_WITH_EXCEPTION(zero, func(in.min_denormal, in.max_normal),
                                   FE_UNDERFLOW | FE_INEXACT);
       EXPECT_MATH_ERRNO(ERANGE);
-      EXPECT_FP_EQ_WITH_EXCEPTION(neg_zero, func(neg_min_denormal, max_normal),
+      EXPECT_FP_EQ_WITH_EXCEPTION(neg_zero,
+                                  func(in.neg_min_denormal, in.max_normal),
                                   FE_UNDERFLOW | FE_INEXACT);
       EXPECT_MATH_ERRNO(ERANGE);
     }
 
     if (ForceRoundingMode r(RoundingMode::Downward); r.success) {
-      EXPECT_FP_EQ_WITH_EXCEPTION(max_normal, func(max_normal, min_normal),
+      EXPECT_FP_EQ_WITH_EXCEPTION(max_normal,
+                                  func(in.max_normal, in.min_normal),
                                   FE_OVERFLOW | FE_INEXACT);
-      EXPECT_FP_EQ_WITH_EXCEPTION(-inf, func(neg_max_normal, min_denormal),
+      EXPECT_FP_EQ_WITH_EXCEPTION(-inf,
+                                  func(in.neg_max_normal, in.min_denormal),
                                   FE_OVERFLOW | FE_INEXACT);
       EXPECT_MATH_ERRNO(ERANGE);
 
-      EXPECT_FP_EQ_WITH_EXCEPTION(zero, func(min_denormal, max_normal),
+      EXPECT_FP_EQ_WITH_EXCEPTION(zero, func(in.min_denormal, in.max_normal),
                                   FE_UNDERFLOW | FE_INEXACT);
       EXPECT_MATH_ERRNO(ERANGE);
       EXPECT_FP_EQ_WITH_EXCEPTION(neg_min_denormal,
-                                  func(neg_min_denormal, max_normal),
+                                  func(in.neg_min_denormal, in.max_normal),
                                   FE_UNDERFLOW | FE_INEXACT);
       EXPECT_MATH_ERRNO(ERANGE);
     }
 
     if (ForceRoundingMode r(RoundingMode::Upward); r.success) {
-      EXPECT_FP_EQ_WITH_EXCEPTION(inf, func(max_normal, min_normal),
+      EXPECT_FP_EQ_WITH_EXCEPTION(inf, func(in.max_normal, in.min_normal),
                                   FE_OVERFLOW | FE_INEXACT);
       EXPECT_MATH_ERRNO(ERANGE);
       EXPECT_FP_EQ_WITH_EXCEPTION(neg_max_normal,
-                                  func(neg_max_normal, min_denormal),
+                                  func(in.neg_max_normal, in.min_denormal),
                                   FE_OVERFLOW | FE_INEXACT);
 
-      EXPECT_FP_EQ_WITH_EXCEPTION(min_denormal, func(min_denormal, max_normal),
+      EXPECT_FP_EQ_WITH_EXCEPTION(min_denormal,
+                                  func(in.min_denormal, in.max_normal),
                                   FE_UNDERFLOW | FE_INEXACT);
       EXPECT_MATH_ERRNO(ERANGE);
-      EXPECT_FP_EQ_WITH_EXCEPTION(neg_zero, func(neg_min_denormal, max_normal),
+      EXPECT_FP_EQ_WITH_EXCEPTION(neg_zero,
+                                  func(in.neg_min_denormal, in.max_normal),
                                   FE_UNDERFLOW | FE_INEXACT);
       EXPECT_MATH_ERRNO(ERANGE);
     }
diff --git a/libc/test/src/math/smoke/FModTest.h b/libc/test/src/math/smoke/FModTest.h
index 0a4227da83f81d..ad9688fc01e7c1 100644
--- a/libc/test/src/math/smoke/FModTest.h
+++ b/libc/test/src/math/smoke/FModTest.h
@@ -108,61 +108,61 @@ class FmodTest : public LIBC_NAMESPACE::testing::FEnvSafeTest {
     TEST_SPECIAL(T(3.0), neg_inf, T(3.0), false, 0);
 
     TEST_SPECIAL(zero, aNaN, aNaN, false, 0);
-    TEST_SPECIAL(zero, -aNaN, aNaN, false, 0);
+    TEST_SPECIAL(zero, neg_aNaN, aNaN, false, 0);
     TEST_SPECIAL(neg_zero, aNaN, aNaN, false, 0);
-    TEST_SPECIAL(neg_zero, -aNaN, aNaN, false, 0);
+    TEST_SPECIAL(neg_zero, neg_aNaN, aNaN, false, 0);
     TEST_SPECIAL(T(1.0), aNaN, aNaN, false, 0);
-    TEST_SPECIAL(T(1.0), -aNaN, aNaN, false, 0);
+    TEST_SPECIAL(T(1.0), neg_aNaN, aNaN, false, 0);
     TEST_SPECIAL(inf, aNaN, aNaN, false, 0);
-    TEST_SPECIAL(inf, -aNaN, aNaN, false, 0);
+    TEST_SPECIAL(inf, neg_aNaN, aNaN, false, 0);
     TEST_SPECIAL(neg_inf, aNaN, aNaN, false, 0);
-    TEST_SPECIAL(neg_inf, -aNaN, aNaN, false, 0);
+    TEST_SPECIAL(neg_inf, neg_aNaN, aNaN, false, 0);
     TEST_SPECIAL(zero, sNaN, aNaN, false, FE_INVALID);
-    TEST_SPECIAL(zero, -sNaN, aNaN, false, FE_INVALID);
+    TEST_SPECIAL(zero, neg_sNaN, aNaN, false, FE_INVALID);
     TEST_SPECIAL(neg_zero, sNaN, aNaN, false, FE_INVALID);
-    TEST_SPECIAL(neg_zero, -sNaN, aNaN, false, FE_INVALID);
+    TEST_SPECIAL(neg_zero, neg_sNaN, aNaN, false, FE_INVALID);
     TEST_SPECIAL(T(1.0), sNaN, aNaN, false, FE_INVALID);
-    TEST_SPECIAL(T(1.0), -sNaN, aNaN, false, FE_INVALID);
+    TEST_SPECIAL(T(1.0), neg_sNaN, aNaN, false, FE_INVALID);
     TEST_SPECIAL(inf, sNaN, aNaN, false, FE_INVALID);
-    TEST_SPECIAL(inf, -sNaN, aNaN, false, FE_INVALID);
+    TEST_SPECIAL(inf, neg_sNaN, aNaN, false, FE_INVALID);
     TEST_SPECIAL(neg_inf, sNaN, aNaN, false, FE_INVALID);
-    TEST_SPECIAL(neg_inf, -sNaN, aNaN, false, FE_INVALID);
+    TEST_SPECIAL(neg_inf, neg_sNaN, aNaN, false, FE_INVALID);
     TEST_SPECIAL(aNaN, zero, aNaN, false, 0);
-    TEST_SPECIAL(-aNaN, zero, aNaN, false, 0);
+    TEST_SPECIAL(neg_aNaN, zero, aNaN, false, 0);
     TEST_SPECIAL(aNaN, neg_zero, aNaN, false, 0);
-    TEST_SPECIAL(-aNaN, neg_zero, aNaN, false, 0);
+    TEST_SPECIAL(neg_aNaN, neg_zero, aNaN, false, 0);
     TEST_SPECIAL(aNaN, T(1.0), aNaN, false, 0);
-    TEST_SPECIAL(-aNaN, T(1.0), aNaN, false, 0);
+    TEST_SPECIAL(neg_aNaN, T(1.0), aNaN, false, 0);
     TEST_SPECIAL(aNaN, inf, aNaN, false, 0);
-    TEST_SPECIAL(-aNaN, inf, aNaN, false, 0);
+    TEST_SPECIAL(neg_aNaN, inf, aNaN, false, 0);
     TEST_SPECIAL(aNaN, neg_inf, aNaN, false, 0);
-    TEST_SPECIAL(-aNaN, neg_inf, aNaN, false, 0);
+    TEST_SPECIAL(neg_aNaN, neg_inf, aNaN, false, 0);
     TEST_SPECIAL(sNaN, zero, aNaN, false, FE_INVALID);
-    TEST_SPECIAL(-sNaN, zero, aNaN, false, FE_INVALID);
+    TEST_SPECIAL(neg_sNaN, zero, aNaN, false, FE_INVALID);
     TEST_SPECIAL(sNaN, neg_zero, aNaN, false, FE_INVALID);
-    TEST_SPECIAL(-sNaN, neg_zero, aNaN, false, FE_INVALID);
+    TEST_SPECIAL(neg_sNaN, neg_zero, aNaN, false, FE_INVALID);
     TEST_SPECIAL(sNaN, T(1.0), aNaN, false, FE_INVALID);
-    TEST_SPECIAL(-sNaN, T(1.0), aNaN, false, FE_INVALID);
+    TEST_SPECIAL(neg_sNaN, T(1.0), aNaN, false, FE_INVALID);
     TEST_SPECIAL(sNaN, inf, aNaN, false, FE_INVALID);
-    TEST_SPECIAL(-sNaN, inf, aNaN, false, FE_INVALID);
+    TEST_SPECIAL(neg_sNaN, inf, aNaN, false, FE_INVALID);
     TEST_SPECIAL(sNaN, neg_inf, aNaN, false, FE_INVALID);
-    TEST_SPECIAL(-sNaN, neg_inf, aNaN, false, FE_INVALID);
+    TEST_SPECIAL(neg_sNaN, neg_inf, aNaN, false, FE_INVALID);
     TEST_SPECIAL(aNaN, aNaN, aNaN, false, 0);
-    TEST_SPECIAL(aNaN, -aNaN, aNaN, false, 0);
-    TEST_SPECIAL(-aNaN, aNaN, aNaN, false, 0);
-    TEST_SPECIAL(-aNaN, -aNaN, aNaN, false, 0);
+    TEST_SPECIAL(aNaN, neg_aNaN, aNaN, false, 0);
+    TEST_SPECIAL(neg_aNaN, aNaN, aNaN, false, 0);
+    TEST_SPECIAL(neg_aNaN, neg_aNaN, aNaN, false, 0);
     TEST_SPECIAL(aNaN, sNaN, aNaN, false, FE_INVALID);
-    TEST_SPECIAL(aNaN, -sNaN, aNaN, false, FE_INVALID);
-    TEST_SPECIAL(-aNaN, sNaN, aNaN, false, FE_INVALID);
-    TEST_SPECIAL(-aNaN, -sNaN, aNaN, false, FE_INVALID);
+    TEST_SPECIAL(aNaN, neg_sNaN, aNaN, false, FE_INVALID);
+    TEST_SPECIAL(neg_aNaN, sNaN, aNaN, false, FE_INVALID);
+    TEST_SPECIAL(neg_aNaN, neg_sNaN, aNaN, false, FE_INVALID);
     TEST_SPECIAL(sNaN, aNaN, aNaN, false, FE_INVALID);
-    TEST_SPECIAL(sNaN, -aNaN, aNaN, false, FE_INVALID);
-    TEST_SPECIAL(-sNaN, aNaN, aNaN, false, FE_INVALID);
-    TEST_SPECIAL(-sNaN, -aNaN, aNaN, false, FE_INVALID);
+    TEST_SPECIAL(sNaN, neg_aNaN, aNaN, false, FE_INVALID);
+    TEST_SPECIAL(neg_sNaN, aNaN, aNaN, false, FE_INVALID);
+    TEST_SPECIAL(neg_sNaN, neg_aNaN, aNaN, false, FE_INVALID);
     TEST_SPECIAL(sNaN, sNaN, aNaN, false, FE_INVALID);
-    TEST_SPECIAL(sNaN, -sNaN, aNaN, false, FE_INVALID);
-    TEST_SPECIAL(-sNaN, sNaN, aNaN, false, FE_INVALID);
-    TEST_SPECIAL(-sNaN, -sNaN, aNaN, false, FE_INVALID);
+    TEST_SPECIAL(sNaN, neg_sNaN, aNaN, false, FE_INVALID);
+    TEST_SPECIAL(neg_sNaN, sNaN, aNaN, false, FE_INVALID);
+    TEST_SPECIAL(neg_sNaN, neg_sNaN, aNaN, false, FE_INVALID);
 
     TEST_SPECIAL(T(6.5), T(2.25), T(2.0), false, 0);
     TEST_SPECIAL(T(-6.5), T(2.25), T(-2.0), false, 0);
diff --git a/libc/test/src/math/smoke/FmaTest.h b/libc/test/src/math/smoke/FmaTest.h
index bf6d06d698fde5..41093422d51b2e 100644
--- a/libc/test/src/math/smoke/FmaTest.h
+++ b/libc/test/src/math/smoke/FmaTest.h
@@ -9,6 +9,9 @@
 #ifndef LLVM_LIBC_TEST_SRC_MATH_FMATEST_H
 #define LLVM_LIBC_TEST_SRC_MATH_FMATEST_H
 
+#include "src/__support/CPP/type_traits.h"
+#include "src/__support/FPUtil/cast.h"
+#include "src/__support/macros/properties/types.h"
 #include "test/UnitTest/FEnvSafeTest.h"
 #include "test/UnitTest/FPMatcher.h"
 #include "test/UnitTest/Test.h"
@@ -37,6 +40,11 @@ class FmaTestTemplate : public LIBC_NAMESPACE::testing::FEnvSafeTest {
   OutConstants out;
   InConstants in;
 
+  const InType in_out_min_normal =
+      LIBC_NAMESPACE::fputil::cast<InType>(out.min_normal);
+  const InType in_out_min_denormal =
+      LIBC_NAMESPACE::fputil::cast<InType>(out.min_denormal);
+
 public:
   using FmaFunc = OutType (*)(InType, InType, InType);
 
@@ -52,7 +60,7 @@ class FmaTestTemplate : public LIBC_NAMESPACE::testing::FEnvSafeTest {
 
     // Test underflow rounding up.
     EXPECT_FP_EQ(OutFPBits(OutStorageType(2)).get_val(),
-                 func(OutType(0.5), out.min_denormal, out.min_denormal));
+                 func(InType(0.5), in_out_min_denormal, in_out_min_denormal));
 
     if constexpr (sizeof(OutType) < sizeof(InType)) {
       EXPECT_FP_EQ(out.zero,
@@ -63,8 +71,9 @@ class FmaTestTemplate : public LIBC_NAMESPACE::testing::FEnvSafeTest {
     OutType v = OutFPBits(static_cast<OutStorageType>(OUT_MIN_NORMAL_U +
                                                       OutStorageType(1)))
                     .get_val();
-    EXPECT_FP_EQ(v, func(OutType(1) / OutType(OUT_MIN_NORMAL_U << 1), v,
-                         out.min_normal));
+    EXPECT_FP_EQ(v, func(InType(1) / InType(OUT_MIN_NORMAL_U << 1),
+                         LIBC_NAMESPACE::fputil::cast<InType>(v),
+                         in_out_min_normal));
 
     if constexpr (sizeof(OutType) < sizeof(InType)) {
       InFPBits tmp = InFPBits::one();
@@ -74,12 +83,21 @@ class FmaTestTemplate : public LIBC_NAMESPACE::testing::FEnvSafeTest {
       InType v = InFPBits(static_cast<InStorageType>(IN_MIN_NORMAL_U +
                                                      InStorageType(1)))
                      .get_val();
-      EXPECT_FP_EQ(out.min_normal, func(reciprocal_value, v, out.min_normal));
+      EXPECT_FP_EQ(out.min_normal,
+                   func(reciprocal_value, v, in_out_min_normal));
     }
 
     // Test overflow.
     OutType z = out.max_normal;
-    EXPECT_FP_EQ_ALL_ROUNDING(OutType(0.75) * z, func(InType(1.75), z, -z));
+    InType in_z = LIBC_NAMESPACE::fputil::cast<InType>(out.max_normal);
+#if defined(LIBC_TYPES_HAS_FLOAT16) && !defined(__LIBC_USE_FLOAT16_CONVERSION)
+    // Rounding modes other than the default might not be usable with float16.
+    if constexpr (LIBC_NAMESPACE::cpp::is_same_v<OutType, float16>)
+      EXPECT_FP_EQ(OutType(0.75) * z, func(InType(1.75), in_z, -in_z));
+    else
+#endif
+      EXPECT_FP_EQ_ALL_ROUNDING(OutType(0.75) * z,
+                                func(InType(1.75), in_z, -in_z));
 
     // Exact cancellation.
     EXPECT_FP_EQ_ROUNDING_NEAREST(
diff --git a/libc/test/src/math/smoke/ModfTest.h b/libc/test/src/math/smoke/ModfTest.h
index 6226e5d55f40cc..24cfb1152c2e5f 100644
--- a/libc/test/src/math/smoke/ModfTest.h
+++ b/libc/test/src/math/smoke/ModfTest.h
@@ -97,7 +97,7 @@ class ModfTest : public LIBC_NAMESPACE::testing::FEnvSafeTest {
 
       T integral;
       T frac = func(x, &integral);
-      ASSERT_TRUE(LIBC_NAMESPACE::fputil::abs(frac) < 1.0l);
+      ASSERT_TRUE(LIBC_NAMESPACE::fputil::abs(frac) < T(1.0));
       ASSERT_TRUE(LIBC_NAMESPACE::fputil::trunc(x) == integral);
       ASSERT_TRUE(integral + frac == x);
     }
diff --git a/libc/test/src/math/smoke/MulTest.h b/libc/test/src/math/smoke/MulTest.h
index 0c847e39687b72..c409122397b1d7 100644
--- a/libc/test/src/math/smoke/MulTest.h
+++ b/libc/test/src/math/smoke/MulTest.h
@@ -34,22 +34,22 @@ class MulTest : public LIBC_NAMESPACE::testing::FEnvSafeTest {
   using MulFunc = OutType (*)(InType, InType);
 
   void test_special_numbers(MulFunc func) {
-    EXPECT_FP_IS_NAN(func(aNaN, aNaN));
-    EXPECT_FP_IS_NAN_WITH_EXCEPTION(func(sNaN, sNaN), FE_INVALID);
+    EXPECT_FP_IS_NAN(func(in.aNaN, in.aNaN));
+    EXPECT_FP_IS_NAN_WITH_EXCEPTION(func(in.sNaN, in.sNaN), FE_INVALID);
 
     InType qnan_42 = InFPBits::quiet_nan(Sign::POS, 0x42).get_val();
-    EXPECT_FP_IS_NAN(func(qnan_42, zero));
-    EXPECT_FP_IS_NAN(func(zero, qnan_42));
+    EXPECT_FP_IS_NAN(func(qnan_42, in.zero));
+    EXPECT_FP_IS_NAN(func(in.zero, qnan_42));
 
-    EXPECT_FP_EQ(inf, func(inf, InType(1.0)));
-    EXPECT_FP_EQ(neg_inf, func(neg_inf, InType(1.0)));
-    EXPECT_FP_EQ(neg_inf, func(inf, InType(-1.0)));
-    EXPECT_FP_EQ(inf, func(neg_inf, InType(-1.0)));
+    EXPECT_FP_EQ(inf, func(in.inf, InType(1.0)));
+    EXPECT_FP_EQ(neg_inf, func(in.neg_inf, InType(1.0)));
+    EXPECT_FP_EQ(neg_inf, func(in.inf, InType(-1.0)));
+    EXPECT_FP_EQ(inf, func(in.neg_inf, InType(-1.0)));
 
-    EXPECT_FP_EQ_ALL_ROUNDING(zero, func(zero, zero));
-    EXPECT_FP_EQ_ALL_ROUNDING(zero, func(neg_zero, neg_zero));
-    EXPECT_FP_EQ_ALL_ROUNDING(neg_zero, func(zero, neg_zero));
-    EXPECT_FP_EQ_ALL_ROUNDING(neg_zero, func(neg_zero, zero));
+    EXPECT_FP_EQ_ALL_ROUNDING(zero, func(in.zero, in.zero));
+    EXPECT_FP_EQ_ALL_ROUNDING(zero, func(in.neg_zero, in.neg_zero));
+    EXPECT_FP_EQ_ALL_ROUNDING(neg_zero, func(in.zero, in.neg_zero));
+    EXPECT_FP_EQ_ALL_ROUNDING(neg_zero, func(in.neg_zero, in.zero));
 
     EXPECT_FP_EQ_ALL_ROUNDING(OutType(1.0), func(1.0, 1.0));
     EXPECT_FP_EQ_ALL_ROUNDING(OutType(15.0), func(3.0, 5.0));
@@ -58,20 +58,21 @@ class MulTest : public LIBC_NAMESPACE::testing::FEnvSafeTest {
   }
 
   void test_invalid_operations(MulFunc func) {
-    EXPECT_FP_IS_NAN_WITH_EXCEPTION(func(inf, zero), FE_INVALID);
-    EXPECT_FP_IS_NAN_WITH_EXCEPTION(func(inf, neg_zero), FE_INVALID);
-    EXPECT_FP_IS_NAN_WITH_EXCEPTION(func(neg_inf, zero), FE_INVALID);
-    EXPECT_FP_IS_NAN_WITH_EXCEPTION(func(neg_inf, neg_zero), FE_INVALID);
+    EXPECT_FP_IS_NAN_WITH_EXCEPTION(func(in.inf, in.zero), FE_INVALID);
+    EXPECT_FP_IS_NAN_WITH_EXCEPTION(func(in.inf, in.neg_zero), FE_INVALID);
+    EXPECT_FP_IS_NAN_WITH_EXCEPTION(func(in.neg_inf, in.zero), FE_INVALID);
+    EXPECT_FP_IS_NAN_WITH_EXCEPTION(func(in.neg_inf, in.neg_zero), FE_INVALID);
   }
 
   void test_range_errors(MulFunc func) {
     using namespace LIBC_NAMESPACE::fputil::testing;
 
     if (ForceRoundingMode r(RoundingMode::Nearest); r.success) {
-      EXPECT_FP_EQ_WITH_EXCEPTION(inf, func(max_normal, max_normal),
+      EXPECT_FP_EQ_WITH_EXCEPTION(inf, func(in.max_normal, in.max_normal),
                                   FE_OVERFLOW | FE_INEXACT);
       EXPECT_MATH_ERRNO(ERANGE);
-      EXPECT_FP_EQ_WITH_EXCEPTION(neg_inf, func(neg_max_normal, max_normal),
+      EXPECT_FP_EQ_WITH_EXCEPTION(neg_inf,
+                                  func(in.neg_max_normal, in.max_normal),
                                   FE_OVERFLOW | FE_INEXACT);
       EXPECT_MATH_ERRNO(ERANGE);
 
@@ -85,10 +86,11 @@ class MulTest : public LIBC_NAMESPACE::testing::FEnvSafeTest {
     }
 
     if (ForceRoundingMode r(RoundingMode::TowardZero); r.success) {
-      EXPECT_FP_EQ_WITH_EXCEPTION(max_normal, func(max_normal, max_normal),
+      EXPECT_FP_EQ_WITH_EXCEPTION(max_normal,
+                                  func(in.max_normal, in.max_normal),
                                   FE_OVERFLOW | FE_INEXACT);
       EXPECT_FP_EQ_WITH_EXCEPTION(neg_max_normal,
-                                  func(neg_max_normal, max_normal),
+                                  func(in.neg_max_normal, in.max_normal),
                                   FE_OVERFLOW | FE_INEXACT);
 
       EXPECT_FP_EQ_WITH_EXCEPTION(zero, func(in.min_denormal, in.min_denormal),
@@ -101,9 +103,11 @@ class MulTest : public LIBC_NAMESPACE::testing::FEnvSafeTest {
     }
 
     if (ForceRoundingMode r(RoundingMode::Downward); r.success) {
-      EXPECT_FP_EQ_WITH_EXCEPTION(max_normal, func(max_normal, max_normal),
+      EXPECT_FP_EQ_WITH_EXCEPTION(max_normal,
+                                  func(in.max_normal, in.max_normal),
                                   FE_OVERFLOW | FE_INEXACT);
-      EXPECT_FP_EQ_WITH_EXCEPTION(neg_inf, func(neg_max_normal, max_normal),
+      EXPECT_FP_EQ_WITH_EXCEPTION(neg_inf,
+                                  func(in.neg_max_normal, in.max_normal),
                                   FE_OVERFLOW | FE_INEXACT);
       EXPECT_MATH_ERRNO(ERANGE);
 
@@ -117,11 +121,11 @@ class MulTest : public LIBC_NAMESPACE::testing::FEnvSafeTest {
     }
 
     if (ForceRoundingMode r(RoundingMode::Upward); r.success) {
-      EXPECT_FP_EQ_WITH_EXCEPTION(inf, func(max_normal, max_normal),
+      EXPECT_FP_EQ_WITH_EXCEPTION(inf, func(in.max_normal, in.max_normal),
                                   FE_OVERFLOW | FE_INEXACT);
       EXPECT_MATH_ERRNO(ERANGE);
       EXPECT_FP_EQ_WITH_EXCEPTION(neg_max_normal,
-                                  func(neg_max_normal, max_normal),
+                                  func(in.neg_max_normal, in.max_normal),
                                   FE_OVERFLOW | FE_INEXACT);
 
       EXPECT_FP_EQ_WITH_EXCEPTION(min_denormal,
diff --git a/libc/test/src/math/smoke/NextTowardTest.h b/libc/test/src/math/smoke/NextTowardTest.h
index 5992273d919012..61528f71305db0 100644
--- a/libc/test/src/math/smoke/NextTowardTest.h
+++ b/libc/test/src/math/smoke/NextTowardTest.h
@@ -43,6 +43,8 @@ class NextTowardTestTemplate : public LIBC_NAMESPACE::testing::FEnvSafeTest {
   const T neg_zero = FPBits::zero(Sign::NEG).get_val();
   const T nan = FPBits::quiet_nan().get_val();
 
+  const long double to_inf = ToFPBits::inf(Sign::POS).get_val();
+  const long double to_neg_inf = ToFPBits::inf(Sign::NEG).get_val();
   const long double to_zero = ToFPBits::zero().get_val();
   const long double to_neg_zero = ToFPBits::zero(Sign::NEG).get_val();
   const long double to_nan = ToFPBits::quiet_nan().get_val();
@@ -134,7 +136,7 @@ class NextTowardTestTemplate : public LIBC_NAMESPACE::testing::FEnvSafeTest {
     expected = LIBC_NAMESPACE::cpp::bit_cast<T>(expected_bits);
     ASSERT_FP_EQ_WITH_UNDERFLOW(result, expected);
 
-    result = func(x, inf);
+    result = func(x, to_inf);
     expected_bits = min_normal + 1;
     expected = LIBC_NAMESPACE::cpp::bit_cast<T>(expected_bits);
     ASSERT_FP_EQ(result, expected);
@@ -145,7 +147,7 @@ class NextTowardTestTemplate : public LIBC_NAMESPACE::testing::FEnvSafeTest {
     expected = LIBC_NAMESPACE::cpp::bit_cast<T>(expected_bits);
     ASSERT_FP_EQ_WITH_UNDERFLOW(result, expected);
 
-    result = func(x, -inf);
+    result = func(x, to_neg_inf);
     expected_bits = FPBits::SIGN_MASK + min_normal + 1;
     expected = LIBC_NAMESPACE::cpp::bit_cast<T>(expected_bits);
     ASSERT_FP_EQ(result, expected);
@@ -156,14 +158,14 @@ class NextTowardTestTemplate : public LIBC_NAMESPACE::testing::FEnvSafeTest {
     expected_bits = max_normal - 1;
     expected = LIBC_NAMESPACE::cpp::bit_cast<T>(expected_bits);
     ASSERT_FP_EQ(result, expected);
-    ASSERT_FP_EQ_WITH_OVERFLOW(func(x, inf), inf);
+    ASSERT_FP_EQ_WITH_OVERFLOW(func(x, to_inf), inf);
 
     x = -x;
     result = func(x, 0);
     expected_bits = FPBits::SIGN_MASK + max_normal - 1;
     expected = LIBC_NAMESPACE::cpp::bit_cast<T>(expected_bits);
     ASSERT_FP_EQ(result, expected);
-    ASSERT_FP_EQ_WITH_OVERFLOW(func(x, -inf), -inf);
+    ASSERT_FP_EQ_WITH_OVERFLOW(func(x, to_neg_inf), neg_inf);
 
     // 'from' is infinity.
     x = inf;
@@ -171,14 +173,14 @@ class NextTowardTestTemplate : public LIBC_NAMESPACE::testing::FEnvSafeTest {
     expected_bits = max_normal;
     expected = LIBC_NAMESPACE::cpp::bit_cast<T>(expected_bits);
     ASSERT_FP_EQ(result, expected);
-    ASSERT_FP_EQ(func(x, inf), inf);
+    ASSERT_FP_EQ(func(x, to_inf), inf);
 
     x = neg_inf;
     result = func(x, 0);
     expected_bits = FPBits::SIGN_MASK + max_normal;
     expected = LIBC_NAMESPACE::cpp::bit_cast<T>(expected_bits);
     ASSERT_FP_EQ(result, expected);
-    ASSERT_FP_EQ(func(x, neg_inf), neg_inf);
+    ASSERT_FP_EQ(func(x, to_neg_inf), neg_inf);
 
     // 'from' is a power of 2.
     x = T(32.0);
diff --git a/libc/test/src/math/smoke/SqrtTest.h b/libc/test/src/math/smoke/SqrtTest.h
index ce9f2f85b4604a..b5eaee22fc79dd 100644
--- a/libc/test/src/math/smoke/SqrtTest.h
+++ b/libc/test/src/math/smoke/SqrtTest.h
@@ -15,15 +15,21 @@ class SqrtTest : public LIBC_NAMESPACE::testing::FEnvSafeTest {
 
   DECLARE_SPECIAL_CONSTANTS(OutType)
 
+  struct InConstants {
+    DECLARE_SPECIAL_CONSTANTS(InType)
+  };
+
+  InConstants in;
+
 public:
   typedef OutType (*SqrtFunc)(InType);
 
   void test_special_numbers(SqrtFunc func) {
-    ASSERT_FP_EQ(aNaN, func(aNaN));
-    ASSERT_FP_EQ(inf, func(inf));
-    ASSERT_FP_EQ(aNaN, func(neg_inf));
-    ASSERT_FP_EQ(zero, func(zero));
-    ASSERT_FP_EQ(neg_zero, func(neg_zero));
+    ASSERT_FP_EQ(aNaN, func(in.aNaN));
+    ASSERT_FP_EQ(inf, func(in.inf));
+    ASSERT_FP_EQ(aNaN, func(in.neg_inf));
+    ASSERT_FP_EQ(zero, func(in.zero));
+    ASSERT_FP_EQ(neg_zero, func(in.neg_zero));
     ASSERT_FP_EQ(aNaN, func(InType(-1.0)));
     ASSERT_FP_EQ(OutType(1.0), func(InType(1.0)));
     ASSERT_FP_EQ(OutType(2.0), func(InType(4.0)));
diff --git a/libc/test/src/math/smoke/SubTest.h b/libc/test/src/math/smoke/SubTest.h
index 9ee4220b382085..b6ad7770a90598 100644
--- a/libc/test/src/math/smoke/SubTest.h
+++ b/libc/test/src/math/smoke/SubTest.h
@@ -33,32 +33,32 @@ class SubTest : public LIBC_NAMESPACE::testing::FEnvSafeTest {
   using SubFunc = OutType (*)(InType, InType);
 
   void test_special_numbers(SubFunc func) {
-    EXPECT_FP_IS_NAN(func(aNaN, aNaN));
-    EXPECT_FP_IS_NAN_WITH_EXCEPTION(func(sNaN, sNaN), FE_INVALID);
+    EXPECT_FP_IS_NAN(func(in.aNaN, in.aNaN));
+    EXPECT_FP_IS_NAN_WITH_EXCEPTION(func(in.sNaN, in.sNaN), FE_INVALID);
 
     InType qnan_42 = InFPBits::quiet_nan(Sign::POS, 0x42).get_val();
-    EXPECT_FP_IS_NAN(func(qnan_42, zero));
-    EXPECT_FP_IS_NAN(func(zero, qnan_42));
+    EXPECT_FP_IS_NAN(func(qnan_42, in.zero));
+    EXPECT_FP_IS_NAN(func(in.zero, qnan_42));
 
-    EXPECT_FP_EQ(inf, func(inf, zero));
-    EXPECT_FP_EQ(neg_inf, func(neg_inf, zero));
-    EXPECT_FP_EQ(inf, func(inf, neg_zero));
-    EXPECT_FP_EQ(neg_inf, func(neg_inf, neg_zero));
+    EXPECT_FP_EQ(inf, func(in.inf, in.zero));
+    EXPECT_FP_EQ(neg_inf, func(in.neg_inf, in.zero));
+    EXPECT_FP_EQ(inf, func(in.inf, in.neg_zero));
+    EXPECT_FP_EQ(neg_inf, func(in.neg_inf, in.neg_zero));
   }
 
   void test_invalid_operations(SubFunc func) {
-    EXPECT_FP_IS_NAN_WITH_EXCEPTION(func(inf, inf), FE_INVALID);
-    EXPECT_FP_IS_NAN_WITH_EXCEPTION(func(neg_inf, neg_inf), FE_INVALID);
+    EXPECT_FP_IS_NAN_WITH_EXCEPTION(func(in.inf, in.inf), FE_INVALID);
+    EXPECT_FP_IS_NAN_WITH_EXCEPTION(func(in.neg_inf, in.neg_inf), FE_INVALID);
   }
 
   void test_range_errors(SubFunc func) {
     using namespace LIBC_NAMESPACE::fputil::testing;
 
     if (ForceRoundingMode r(RoundingMode::Nearest); r.success) {
-      EXPECT_FP_EQ_WITH_EXCEPTION(inf, func(max_normal, neg_max_normal),
+      EXPECT_FP_EQ_WITH_EXCEPTION(inf, func(in.max_normal, in.neg_max_normal),
                                   FE_OVERFLOW | FE_INEXACT);
       EXPECT_MATH_ERRNO(ERANGE);
-      EXPECT_FP_EQ_WITH_EXCEPTION(-inf, func(neg_max_normal, max_normal),
+      EXPECT_FP_EQ_WITH_EXCEPTION(-inf, func(in.neg_max_normal, in.max_normal),
                                   FE_OVERFLOW | FE_INEXACT);
       EXPECT_MATH_ERRNO(ERANGE);
 
@@ -73,10 +73,11 @@ class SubTest : public LIBC_NAMESPACE::testing::FEnvSafeTest {
     }
 
     if (ForceRoundingMode r(RoundingMode::TowardZero); r.success) {
-      EXPECT_FP_EQ_WITH_EXCEPTION(max_normal, func(max_normal, neg_max_normal),
+      EXPECT_FP_EQ_WITH_EXCEPTION(max_normal,
+                                  func(in.max_normal, in.neg_max_normal),
                                   FE_OVERFLOW | FE_INEXACT);
       EXPECT_FP_EQ_WITH_EXCEPTION(neg_max_normal,
-                                  func(neg_max_normal, max_normal),
+                                  func(in.neg_max_normal, in.max_normal),
                                   FE_OVERFLOW | FE_INEXACT);
 
       EXPECT_FP_EQ_WITH_EXCEPTION(zero,
@@ -90,9 +91,10 @@ class SubTest : public LIBC_NAMESPACE::testing::FEnvSafeTest {
     }
 
     if (ForceRoundingMode r(RoundingMode::Downward); r.success) {
-      EXPECT_FP_EQ_WITH_EXCEPTION(max_normal, func(max_normal, neg_max_normal),
+      EXPECT_FP_EQ_WITH_EXCEPTION(max_normal,
+                                  func(in.max_normal, in.neg_max_normal),
                                   FE_OVERFLOW | FE_INEXACT);
-      EXPECT_FP_EQ_WITH_EXCEPTION(-inf, func(neg_max_normal, max_normal),
+      EXPECT_FP_EQ_WITH_EXCEPTION(-inf, func(in.neg_max_normal, in.max_normal),
                                   FE_OVERFLOW | FE_INEXACT);
       EXPECT_MATH_ERRNO(ERANGE);
 
@@ -107,11 +109,11 @@ class SubTest : public LIBC_NAMESPACE::testing::FEnvSafeTest {
     }
 
     if (ForceRoundingMode r(RoundingMode::Upward); r.success) {
-      EXPECT_FP_EQ_WITH_EXCEPTION(inf, func(max_normal, neg_max_normal),
+      EXPECT_FP_EQ_WITH_EXCEPTION(inf, func(in.max_normal, in.neg_max_normal),
                                   FE_OVERFLOW | FE_INEXACT);
       EXPECT_MATH_ERRNO(ERANGE);
       EXPECT_FP_EQ_WITH_EXCEPTION(neg_max_normal,
-                                  func(neg_max_normal, max_normal),
+                                  func(in.neg_max_normal, in.max_normal),
                                   FE_OVERFLOW | FE_INEXACT);
 
       EXPECT_FP_EQ_WITH_EXCEPTION(min_denormal,
@@ -126,7 +128,7 @@ class SubTest : public LIBC_NAMESPACE::testing::FEnvSafeTest {
   }
 
   void test_inexact_results(SubFunc func) {
-    func(InType(1.0), min_denormal);
+    func(InType(1.0), in.min_denormal);
     EXPECT_FP_EXCEPTION(FE_INEXACT);
   }
 };
diff --git a/libc/test/src/math/smoke/exp10f16_test.cpp b/libc/test/src/math/smoke/exp10f16_test.cpp
index 006dfafa8aa141..1c4ef2aa08a70a 100644
--- a/libc/test/src/math/smoke/exp10f16_test.cpp
+++ b/libc/test/src/math/smoke/exp10f16_test.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "hdr/fenv_macros.h"
+#include "src/__support/FPUtil/cast.h"
 #include "src/errno/libc_errno.h"
 #include "src/math/exp10f16.h"
 #include "test/UnitTest/FPMatcher.h"
@@ -26,15 +27,14 @@ TEST_F(LlvmLibcExp10f16Test, SpecialNumbers) {
   EXPECT_FP_EQ_ALL_ROUNDING(inf, LIBC_NAMESPACE::exp10f16(inf));
   EXPECT_MATH_ERRNO(0);
 
-  EXPECT_FP_EQ_ALL_ROUNDING(static_cast<float16>(zero),
-                            LIBC_NAMESPACE::exp10f16(neg_inf));
+  EXPECT_FP_EQ_ALL_ROUNDING(zero, LIBC_NAMESPACE::exp10f16(neg_inf));
   EXPECT_MATH_ERRNO(0);
 
-  EXPECT_FP_EQ_ALL_ROUNDING(static_cast<float16>(1.0f),
+  EXPECT_FP_EQ_ALL_ROUNDING(LIBC_NAMESPACE::fputil::cast<float16>(1.0f),
                             LIBC_NAMESPACE::exp10f16(zero));
   EXPECT_MATH_ERRNO(0);
 
-  EXPECT_FP_EQ_ALL_ROUNDING(static_cast<float16>(1.0f),
+  EXPECT_FP_EQ_ALL_ROUNDING(LIBC_NAMESPACE::fputil::cast<float16>(1.0f),
                             LIBC_NAMESPACE::exp10f16(neg_zero));
   EXPECT_MATH_ERRNO(0);
 }
@@ -47,7 +47,8 @@ TEST_F(LlvmLibcExp10f16Test, Overflow) {
   EXPECT_MATH_ERRNO(ERANGE);
 
   EXPECT_FP_EQ_WITH_EXCEPTION(
-      inf, LIBC_NAMESPACE::exp10f16(static_cast<float16>(5.0)), FE_OVERFLOW);
+      inf, LIBC_NAMESPACE::exp10f16(LIBC_NAMESPACE::fputil::cast<float16>(5.0)),
+      FE_OVERFLOW);
   EXPECT_MATH_ERRNO(ERANGE);
 }
 
@@ -59,7 +60,8 @@ TEST_F(LlvmLibcExp10f16Test, Underflow) {
   EXPECT_MATH_ERRNO(ERANGE);
 
   EXPECT_FP_EQ_WITH_EXCEPTION(
-      zero, LIBC_NAMESPACE::exp10f16(static_cast<float16>(-8.0)),
+      zero,
+      LIBC_NAMESPACE::exp10f16(LIBC_NAMESPACE::fputil::cast<float16>(-8.0)),
       FE_UNDERFLOW | FE_INEXACT);
   EXPECT_MATH_ERRNO(ERANGE);
 }
diff --git a/libc/test/src/math/smoke/exp2f16_test.cpp b/libc/test/src/math/smoke/exp2f16_test.cpp
index cd87e6134557a5..f69b33a3cf37fe 100644
--- a/libc/test/src/math/smoke/exp2f16_test.cpp
+++ b/libc/test/src/math/smoke/exp2f16_test.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "hdr/fenv_macros.h"
+#include "src/__support/FPUtil/cast.h"
 #include "src/errno/libc_errno.h"
 #include "src/math/exp2f16.h"
 #include "test/UnitTest/FPMatcher.h"
@@ -26,15 +27,14 @@ TEST_F(LlvmLibcExp2f16Test, SpecialNumbers) {
   EXPECT_FP_EQ_ALL_ROUNDING(inf, LIBC_NAMESPACE::exp2f16(inf));
   EXPECT_MATH_ERRNO(0);
 
-  EXPECT_FP_EQ_ALL_ROUNDING(static_cast<float16>(zero),
-                            LIBC_NAMESPACE::exp2f16(neg_inf));
+  EXPECT_FP_EQ_ALL_ROUNDING(zero, LIBC_NAMESPACE::exp2f16(neg_inf));
   EXPECT_MATH_ERRNO(0);
 
-  EXPECT_FP_EQ_ALL_ROUNDING(static_cast<float16>(1.0f),
+  EXPECT_FP_EQ_ALL_ROUNDING(LIBC_NAMESPACE::fputil::cast<float16>(1.0f),
                             LIBC_NAMESPACE::exp2f16(zero));
   EXPECT_MATH_ERRNO(0);
 
-  EXPECT_FP_EQ_ALL_ROUNDING(static_cast<float16>(1.0f),
+  EXPECT_FP_EQ_ALL_ROUNDING(LIBC_NAMESPACE::fputil::cast<float16>(1.0f),
                             LIBC_NAMESPACE::exp2f16(neg_zero));
   EXPECT_MATH_ERRNO(0);
 }
@@ -47,7 +47,8 @@ TEST_F(LlvmLibcExp2f16Test, Overflow) {
   EXPECT_MATH_ERRNO(ERANGE);
 
   EXPECT_FP_EQ_WITH_EXCEPTION(
-      inf, LIBC_NAMESPACE::exp2f16(static_cast<float16>(16.0)), FE_OVERFLOW);
+      inf, LIBC_NAMESPACE::exp2f16(LIBC_NAMESPACE::fputil::cast<float16>(16.0)),
+      FE_OVERFLOW);
   EXPECT_MATH_ERRNO(ERANGE);
 }
 
@@ -59,7 +60,8 @@ TEST_F(LlvmLibcExp2f16Test, Underflow) {
   EXPECT_MATH_ERRNO(ERANGE);
 
   EXPECT_FP_EQ_WITH_EXCEPTION(
-      zero, LIBC_NAMESPACE::exp2f16(static_cast<float16>(-25.0)),
+      zero,
+      LIBC_NAMESPACE::exp2f16(LIBC_NAMESPACE::fputil::cast<float16>(-25.0)),
       FE_UNDERFLOW | FE_INEXACT);
   EXPECT_MATH_ERRNO(ERANGE);
 }
diff --git a/libc/test/src/math/smoke/expf16_test.cpp b/libc/test/src/math/smoke/expf16_test.cpp
index 969870fe247bc2..ab745a3cf6f563 100644
--- a/libc/test/src/math/smoke/expf16_test.cpp
+++ b/libc/test/src/math/smoke/expf16_test.cpp
@@ -8,6 +8,7 @@
 
 #include "hdr/errno_macros.h"
 #include "hdr/fenv_macros.h"
+#include "src/__support/FPUtil/cast.h"
 #include "src/errno/libc_errno.h"
 #include "src/math/expf16.h"
 #include "test/UnitTest/FPMatcher.h"
@@ -27,15 +28,14 @@ TEST_F(LlvmLibcExpf16Test, SpecialNumbers) {
   EXPECT_FP_EQ_ALL_ROUNDING(inf, LIBC_NAMESPACE::expf16(inf));
   EXPECT_MATH_ERRNO(0);
 
-  EXPECT_FP_EQ_ALL_ROUNDING(static_cast<float16>(zero),
-                            LIBC_NAMESPACE::expf16(neg_inf));
+  EXPECT_FP_EQ_ALL_ROUNDING(zero, LIBC_NAMESPACE::expf16(neg_inf));
   EXPECT_MATH_ERRNO(0);
 
-  EXPECT_FP_EQ_ALL_ROUNDING(static_cast<float16>(1.0f),
+  EXPECT_FP_EQ_ALL_ROUNDING(LIBC_NAMESPACE::fputil::cast<float16>(1.0f),
                             LIBC_NAMESPACE::expf16(zero));
   EXPECT_MATH_ERRNO(0);
 
-  EXPECT_FP_EQ_ALL_ROUNDING(static_cast<float16>(1.0f),
+  EXPECT_FP_EQ_ALL_ROUNDING(LIBC_NAMESPACE::fputil::cast<float16>(1.0f),
                             LIBC_NAMESPACE::expf16(neg_zero));
   EXPECT_MATH_ERRNO(0);
 }
@@ -48,7 +48,8 @@ TEST_F(LlvmLibcExpf16Test, Overflow) {
   EXPECT_MATH_ERRNO(ERANGE);
 
   EXPECT_FP_EQ_WITH_EXCEPTION(
-      inf, LIBC_NAMESPACE::expf16(static_cast<float16>(12.0)), FE_OVERFLOW);
+      inf, LIBC_NAMESPACE::expf16(LIBC_NAMESPACE::fputil::cast<float16>(12.0)),
+      FE_OVERFLOW);
   EXPECT_MATH_ERRNO(ERANGE);
 }
 
@@ -60,7 +61,8 @@ TEST_F(LlvmLibcExpf16Test, Underflow) {
   EXPECT_MATH_ERRNO(ERANGE);
 
   EXPECT_FP_EQ_WITH_EXCEPTION(
-      zero, LIBC_NAMESPACE::expf16(static_cast<float16>(-18.0)),
+      zero,
+      LIBC_NAMESPACE::expf16(LIBC_NAMESPACE::fputil::cast<float16>(-18.0)),
       FE_UNDERFLOW | FE_INEXACT);
   EXPECT_MATH_ERRNO(ERANGE);
 }
diff --git a/libc/test/src/math/smoke/expm1f16_test.cpp b/libc/test/src/math/smoke/expm1f16_test.cpp
index 3bdbaad2279416..f297c5dfc3c7e1 100644
--- a/libc/test/src/math/smoke/expm1f16_test.cpp
+++ b/libc/test/src/math/smoke/expm1f16_test.cpp
@@ -8,6 +8,7 @@
 
 #include "hdr/errno_macros.h"
 #include "hdr/fenv_macros.h"
+#include "src/__support/FPUtil/cast.h"
 #include "src/errno/libc_errno.h"
 #include "src/math/expm1f16.h"
 #include "test/UnitTest/FPMatcher.h"
@@ -27,7 +28,7 @@ TEST_F(LlvmLibcExpm1f16Test, SpecialNumbers) {
   EXPECT_FP_EQ_ALL_ROUNDING(inf, LIBC_NAMESPACE::expm1f16(inf));
   EXPECT_MATH_ERRNO(0);
 
-  EXPECT_FP_EQ_ALL_ROUNDING(static_cast<float16>(-1.0),
+  EXPECT_FP_EQ_ALL_ROUNDING(LIBC_NAMESPACE::fputil::cast<float16>(-1.0),
                             LIBC_NAMESPACE::expm1f16(neg_inf));
   EXPECT_MATH_ERRNO(0);
 
@@ -46,7 +47,7 @@ TEST_F(LlvmLibcExpm1f16Test, Overflow) {
   EXPECT_MATH_ERRNO(ERANGE);
 
   // round(16 * log(2), HP, RN);
-  float16 x = static_cast<float16>(0x1.63p+3);
+  float16 x = LIBC_NAMESPACE::fputil::cast<float16>(0x1.63p+3);
 
   EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_NEAREST(inf, LIBC_NAMESPACE::expm1f16(x),
                                                FE_OVERFLOW | FE_INEXACT);
@@ -68,41 +69,44 @@ TEST_F(LlvmLibcExpm1f16Test, Overflow) {
 TEST_F(LlvmLibcExpm1f16Test, ResultNearNegOne) {
   LIBC_NAMESPACE::libc_errno = 0;
 
-  EXPECT_FP_EQ_WITH_EXCEPTION(static_cast<float16>(-1.0),
+  EXPECT_FP_EQ_WITH_EXCEPTION(LIBC_NAMESPACE::fputil::cast<float16>(-1.0),
                               LIBC_NAMESPACE::expm1f16(neg_max_normal),
                               FE_INEXACT);
 
   // round(-11 * log(2), HP, RN);
-  float16 x = static_cast<float16>(-0x1.e8p+2);
+  float16 x = LIBC_NAMESPACE::fputil::cast<float16>(-0x1.e8p+2);
 
   EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_NEAREST(
-      static_cast<float16>(-0x1.ffcp-1), LIBC_NAMESPACE::expm1f16(x),
-      FE_INEXACT);
+      LIBC_NAMESPACE::fputil::cast<float16>(-0x1.ffcp-1),
+      LIBC_NAMESPACE::expm1f16(x), FE_INEXACT);
 
-  EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_UPWARD(static_cast<float16>(-0x1.ffcp-1),
-                                              LIBC_NAMESPACE::expm1f16(x),
-                                              FE_INEXACT);
+  EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_UPWARD(
+      LIBC_NAMESPACE::fputil::cast<float16>(-0x1.ffcp-1),
+      LIBC_NAMESPACE::expm1f16(x), FE_INEXACT);
 
   EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_DOWNWARD(
-      static_cast<float16>(-1.0), LIBC_NAMESPACE::expm1f16(x), FE_INEXACT);
+      LIBC_NAMESPACE::fputil::cast<float16>(-1.0), LIBC_NAMESPACE::expm1f16(x),
+      FE_INEXACT);
 
   EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_TOWARD_ZERO(
-      static_cast<float16>(-0x1.ffcp-1), LIBC_NAMESPACE::expm1f16(x),
-      FE_INEXACT);
+      LIBC_NAMESPACE::fputil::cast<float16>(-0x1.ffcp-1),
+      LIBC_NAMESPACE::expm1f16(x), FE_INEXACT);
 
-  x = static_cast<float16>(-0x1.0a4p+3);
+  x = LIBC_NAMESPACE::fputil::cast<float16>(-0x1.0a4p+3);
 
   EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_NEAREST(
-      static_cast<float16>(-1.0), LIBC_NAMESPACE::expm1f16(x), FE_INEXACT);
+      LIBC_NAMESPACE::fputil::cast<float16>(-1.0), LIBC_NAMESPACE::expm1f16(x),
+      FE_INEXACT);
 
-  EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_UPWARD(static_cast<float16>(-0x1.ffcp-1),
-                                              LIBC_NAMESPACE::expm1f16(x),
-                                              FE_INEXACT);
+  EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_UPWARD(
+      LIBC_NAMESPACE::fputil::cast<float16>(-0x1.ffcp-1),
+      LIBC_NAMESPACE::expm1f16(x), FE_INEXACT);
 
   EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_DOWNWARD(
-      static_cast<float16>(-1.0), LIBC_NAMESPACE::expm1f16(x), FE_INEXACT);
+      LIBC_NAMESPACE::fputil::cast<float16>(-1.0), LIBC_NAMESPACE::expm1f16(x),
+      FE_INEXACT);
 
   EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_TOWARD_ZERO(
-      static_cast<float16>(-0x1.ffcp-1), LIBC_NAMESPACE::expm1f16(x),
-      FE_INEXACT);
+      LIBC_NAMESPACE::fputil::cast<float16>(-0x1.ffcp-1),
+      LIBC_NAMESPACE::expm1f16(x), FE_INEXACT);
 }
diff --git a/libc/utils/MPFRWrapper/CMakeLists.txt b/libc/utils/MPFRWrapper/CMakeLists.txt
index be0415d0956fa4..cebf9082271ec5 100644
--- a/libc/utils/MPFRWrapper/CMakeLists.txt
+++ b/libc/utils/MPFRWrapper/CMakeLists.txt
@@ -11,6 +11,7 @@ if(LIBC_TESTS_CAN_USE_MPFR)
     libc.src.__support.CPP.stringstream
     libc.src.__support.CPP.string_view
     libc.src.__support.CPP.type_traits
+    libc.src.__support.FPUtil.cast
     libc.src.__support.FPUtil.fp_bits
     libc.src.__support.FPUtil.fpbits_str
     LibcTest.unit
diff --git a/libc/utils/MPFRWrapper/MPFRUtils.cpp b/libc/utils/MPFRWrapper/MPFRUtils.cpp
index 7ce6a70d093169..27ff1f7190ef95 100644
--- a/libc/utils/MPFRWrapper/MPFRUtils.cpp
+++ b/libc/utils/MPFRWrapper/MPFRUtils.cpp
@@ -13,6 +13,7 @@
 #include "src/__support/CPP/string_view.h"
 #include "src/__support/CPP/stringstream.h"
 #include "src/__support/FPUtil/FPBits.h"
+#include "src/__support/FPUtil/cast.h"
 #include "src/__support/FPUtil/fpbits_str.h"
 #include "src/__support/macros/config.h"
 #include "src/__support/macros/properties/types.h"
@@ -683,7 +684,7 @@ template <> long double MPFRNumber::as<long double>() const {
 template <> float16 MPFRNumber::as<float16>() const {
   // TODO: Either prove that this cast won't cause double-rounding errors, or
   // find a better way to get a float16.
-  return static_cast<float16>(mpfr_get_d(value, mpfr_rounding));
+  return fputil::cast<float16>(mpfr_get_d(value, mpfr_rounding));
 }
 #endif
 

>From 5e6d18c7ceccfe053805fcef07a21001231202f4 Mon Sep 17 00:00:00 2001
From: OverMighty <its.overmighty at gmail.com>
Date: Tue, 20 Aug 2024 19:40:42 +0200
Subject: [PATCH 2/2] Fix CMake target dependencies

---
 libc/src/__support/FPUtil/CMakeLists.txt         | 2 ++
 libc/src/__support/FPUtil/generic/CMakeLists.txt | 1 +
 2 files changed, 3 insertions(+)

diff --git a/libc/src/__support/FPUtil/CMakeLists.txt b/libc/src/__support/FPUtil/CMakeLists.txt
index 90901549946f42..522b4afefd48d6 100644
--- a/libc/src/__support/FPUtil/CMakeLists.txt
+++ b/libc/src/__support/FPUtil/CMakeLists.txt
@@ -92,11 +92,13 @@ add_header_library(
   HDRS
     except_value_utils.h
   DEPENDS
+    .cast
     .fp_bits
     .fenv_impl
     .rounding_mode
     libc.src.__support.CPP.optional
     libc.src.__support.macros.optimization
+    libc.src.__support.macros.properties.cpu_features
     libc.src.__support.macros.properties.types
 )
 
diff --git a/libc/src/__support/FPUtil/generic/CMakeLists.txt b/libc/src/__support/FPUtil/generic/CMakeLists.txt
index b6f58f3fab571a..60434d6f6f11ab 100644
--- a/libc/src/__support/FPUtil/generic/CMakeLists.txt
+++ b/libc/src/__support/FPUtil/generic/CMakeLists.txt
@@ -68,6 +68,7 @@ add_header_library(
     libc.src.__support.FPUtil.fp_bits
     libc.src.__support.FPUtil.rounding_mode
     libc.src.__support.macros.attributes
+    libc.src.__support.macros.optimization
 )
 
 add_header_library(



More information about the libc-commits mailing list