[libc-commits] [libc] [libc][math][c23] Add {f, d}mul{l, f128} and f16mul{, f, l, f128} C23 math functions (PR #98972)

via libc-commits libc-commits at lists.llvm.org
Wed Jul 17 03:04:42 PDT 2024


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

>From 5007943dbddd5b5dc97d1373b0ba80bcca9b5edd Mon Sep 17 00:00:00 2001
From: OverMighty <its.overmighty at gmail.com>
Date: Mon, 15 Jul 2024 23:51:52 +0200
Subject: [PATCH 1/2] [libc][math][c23] Add {f,d}mul{l,f128} and
 f16mul{,f,l,f128} C23 math functions

Part of #93566.

Fixes #94833.
---
 libc/config/linux/aarch64/entrypoints.txt     |   4 +
 libc/config/linux/x86_64/entrypoints.txt      |   8 +
 libc/docs/math/index.rst                      |   6 +-
 libc/spec/llvm_libc_ext.td                    |   8 +
 libc/spec/stdc.td                             |   9 +-
 .../__support/FPUtil/generic/CMakeLists.txt   |  17 ++
 libc/src/__support/FPUtil/generic/mul.h       | 120 ++++++++++++
 libc/src/math/CMakeLists.txt                  |  10 +
 libc/src/math/dmulf128.h                      |  21 +++
 libc/src/math/dmull.h                         |  20 ++
 libc/src/math/f16mul.h                        |  21 +++
 libc/src/math/f16mulf.h                       |  21 +++
 libc/src/math/f16mulf128.h                    |  21 +++
 libc/src/math/f16mull.h                       |  21 +++
 libc/src/math/fmulf128.h                      |  21 +++
 libc/src/math/fmull.h                         |  20 ++
 libc/src/math/generic/CMakeLists.txt          | 108 ++++++++++-
 libc/src/math/generic/dmulf128.cpp            |  20 ++
 libc/src/math/generic/dmull.cpp               |  20 ++
 libc/src/math/generic/f16mul.cpp              |  20 ++
 libc/src/math/generic/f16mulf.cpp             |  20 ++
 libc/src/math/generic/f16mulf128.cpp          |  20 ++
 libc/src/math/generic/f16mull.cpp             |  20 ++
 libc/src/math/generic/fmul.cpp                | 115 +-----------
 libc/src/math/generic/fmulf128.cpp            |  20 ++
 libc/src/math/generic/fmull.cpp               |  20 ++
 libc/test/src/math/CMakeLists.txt             |  80 +++++++-
 libc/test/src/math/FMulTest.h                 | 121 -------------
 libc/test/src/math/MulTest.h                  |  95 ++++++++++
 libc/test/src/math/dmull_test.cpp             |  13 ++
 libc/test/src/math/f16mul_test.cpp            |  13 ++
 libc/test/src/math/f16mulf_test.cpp           |  13 ++
 libc/test/src/math/f16mull_test.cpp           |  13 ++
 libc/test/src/math/fmul_test.cpp              |   8 +-
 libc/test/src/math/fmull_test.cpp             |  13 ++
 libc/test/src/math/smoke/CMakeLists.txt       |  96 +++++++++-
 libc/test/src/math/smoke/FMulTest.h           | 104 -----------
 libc/test/src/math/smoke/MulTest.h            | 171 ++++++++++++++++++
 libc/test/src/math/smoke/dmulf128_test.cpp    |  13 ++
 libc/test/src/math/smoke/dmull_test.cpp       |  13 ++
 libc/test/src/math/smoke/f16mul_test.cpp      |  13 ++
 libc/test/src/math/smoke/f16mulf128_test.cpp  |  13 ++
 libc/test/src/math/smoke/f16mulf_test.cpp     |  13 ++
 libc/test/src/math/smoke/f16mull_test.cpp     |  13 ++
 libc/test/src/math/smoke/fmul_test.cpp        |   8 +-
 libc/test/src/math/smoke/fmulf128_test.cpp    |  13 ++
 libc/test/src/math/smoke/fmull_test.cpp       |  13 ++
 libc/utils/MPFRWrapper/MPFRUtils.cpp          |  21 ++-
 libc/utils/MPFRWrapper/MPFRUtils.h            |   2 +-
 49 files changed, 1242 insertions(+), 364 deletions(-)
 create mode 100644 libc/src/__support/FPUtil/generic/mul.h
 create mode 100644 libc/src/math/dmulf128.h
 create mode 100644 libc/src/math/dmull.h
 create mode 100644 libc/src/math/f16mul.h
 create mode 100644 libc/src/math/f16mulf.h
 create mode 100644 libc/src/math/f16mulf128.h
 create mode 100644 libc/src/math/f16mull.h
 create mode 100644 libc/src/math/fmulf128.h
 create mode 100644 libc/src/math/fmull.h
 create mode 100644 libc/src/math/generic/dmulf128.cpp
 create mode 100644 libc/src/math/generic/dmull.cpp
 create mode 100644 libc/src/math/generic/f16mul.cpp
 create mode 100644 libc/src/math/generic/f16mulf.cpp
 create mode 100644 libc/src/math/generic/f16mulf128.cpp
 create mode 100644 libc/src/math/generic/f16mull.cpp
 create mode 100644 libc/src/math/generic/fmulf128.cpp
 create mode 100644 libc/src/math/generic/fmull.cpp
 delete mode 100644 libc/test/src/math/FMulTest.h
 create mode 100644 libc/test/src/math/MulTest.h
 create mode 100644 libc/test/src/math/dmull_test.cpp
 create mode 100644 libc/test/src/math/f16mul_test.cpp
 create mode 100644 libc/test/src/math/f16mulf_test.cpp
 create mode 100644 libc/test/src/math/f16mull_test.cpp
 create mode 100644 libc/test/src/math/fmull_test.cpp
 delete mode 100644 libc/test/src/math/smoke/FMulTest.h
 create mode 100644 libc/test/src/math/smoke/MulTest.h
 create mode 100644 libc/test/src/math/smoke/dmulf128_test.cpp
 create mode 100644 libc/test/src/math/smoke/dmull_test.cpp
 create mode 100644 libc/test/src/math/smoke/f16mul_test.cpp
 create mode 100644 libc/test/src/math/smoke/f16mulf128_test.cpp
 create mode 100644 libc/test/src/math/smoke/f16mulf_test.cpp
 create mode 100644 libc/test/src/math/smoke/f16mull_test.cpp
 create mode 100644 libc/test/src/math/smoke/fmulf128_test.cpp
 create mode 100644 libc/test/src/math/smoke/fmull_test.cpp

diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index dee6ac673643e..211f2efa012ba 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -356,6 +356,7 @@ set(TARGET_LIBM_ENTRYPOINTS
     libc.src.math.cosf
     libc.src.math.coshf
     libc.src.math.cospif
+    libc.src.math.dmull
     libc.src.math.erff
     libc.src.math.exp
     libc.src.math.exp10
@@ -410,6 +411,7 @@ set(TARGET_LIBM_ENTRYPOINTS
     libc.src.math.fmodf
     libc.src.math.fmodl
     libc.src.math.fmul
+    libc.src.math.fmull
     libc.src.math.frexp
     libc.src.math.frexpf
     libc.src.math.frexpl
@@ -530,6 +532,8 @@ if(LIBC_TYPES_HAS_FLOAT16)
     libc.src.math.f16div
     libc.src.math.f16divf
     libc.src.math.f16fmaf
+    libc.src.math.f16mul
+    libc.src.math.f16mulf
     libc.src.math.f16sqrt
     libc.src.math.f16sqrtf
     libc.src.math.f16sub
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index b6c55e7aa3033..6027f9c613773 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -381,6 +381,7 @@ set(TARGET_LIBM_ENTRYPOINTS
     libc.src.math.cosf
     libc.src.math.coshf
     libc.src.math.cospif
+    libc.src.math.dmull
     libc.src.math.erff
     libc.src.math.exp
     libc.src.math.exp10
@@ -436,6 +437,7 @@ set(TARGET_LIBM_ENTRYPOINTS
     libc.src.math.fmodf
     libc.src.math.fmodl
     libc.src.math.fmul
+    libc.src.math.fmull
     libc.src.math.frexp
     libc.src.math.frexpf
     libc.src.math.frexpl
@@ -560,6 +562,9 @@ if(LIBC_TYPES_HAS_FLOAT16)
     libc.src.math.f16fma
     libc.src.math.f16fmaf
     libc.src.math.f16fmal
+    libc.src.math.f16mul
+    libc.src.math.f16mulf
+    libc.src.math.f16mull
     libc.src.math.f16sqrt
     libc.src.math.f16sqrtf
     libc.src.math.f16sqrtl
@@ -621,6 +626,7 @@ if(LIBC_TYPES_HAS_FLOAT16)
       libc.src.math.f16addf128
       libc.src.math.f16divf128
       libc.src.math.f16fmaf128
+      libc.src.math.f16mulf128
       libc.src.math.f16sqrtf128
       libc.src.math.f16subf128
     )
@@ -633,6 +639,7 @@ if(LIBC_TYPES_HAS_FLOAT128)
     libc.src.math.canonicalizef128
     libc.src.math.ceilf128
     libc.src.math.copysignf128
+    libc.src.math.dmulf128
     libc.src.math.fabsf128
     libc.src.math.fdimf128
     libc.src.math.floorf128
@@ -647,6 +654,7 @@ if(LIBC_TYPES_HAS_FLOAT128)
     libc.src.math.fminimum_numf128
     libc.src.math.fminimumf128
     libc.src.math.fmodf128
+    libc.src.math.fmulf128
     libc.src.math.frexpf128
     libc.src.math.fromfpf128
     libc.src.math.fromfpxf128
diff --git a/libc/docs/math/index.rst b/libc/docs/math/index.rst
index 70412e4ed203d..2411029d10398 100644
--- a/libc/docs/math/index.rst
+++ b/libc/docs/math/index.rst
@@ -120,7 +120,7 @@ Basic Operations
 +------------------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
 | dfma             | N/A              | N/A             |                        | N/A                  |                        | 7.12.14.5              | F.10.11                    |
 +------------------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
-| dmul             | N/A              | N/A             |                        | N/A                  |                        | 7.12.14.3              | F.10.11                    |
+| dmul             | N/A              | N/A             | |check|                | N/A                  | |check|\*              | 7.12.14.3              | F.10.11                    |
 +------------------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
 | dsub             | N/A              | N/A             |                        | N/A                  |                        | 7.12.14.2              | F.10.11                    |
 +------------------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
@@ -130,6 +130,8 @@ Basic Operations
 +------------------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
 | f16fma           | |check|\*        | |check|\*       | |check|\*              | N/A                  | |check|                | 7.12.14.5              | F.10.11                    |
 +------------------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
+| f16mul           | |check|\*        | |check|\*       | |check|\*              | N/A                  | |check|                | 7.12.14.5              | F.10.11                    |
++------------------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
 | f16sub           | |check|\*        | |check|\*       | |check|\*              | N/A                  | |check|                | 7.12.14.2              | F.10.11                    |
 +------------------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
 | fabs             | |check|          | |check|         | |check|                | |check|              | |check|                | 7.12.7.3               | F.10.4.3                   |
@@ -166,7 +168,7 @@ Basic Operations
 +------------------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
 | fmod             | |check|          | |check|         | |check|                | |check|              | |check|                | 7.12.10.1              | F.10.7.1                   |
 +------------------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
-| fmul             | N/A              | |check|         |                        | N/A                  |                        | 7.12.14.3              | F.10.11                    |
+| fmul             | N/A              | |check|         | |check|                | N/A                  | |check|\*              | 7.12.14.3              | F.10.11                    |
 +------------------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
 | frexp            | |check|          | |check|         | |check|                | |check|              | |check|                | 7.12.6.7               | F.10.3.7                   |
 +------------------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
diff --git a/libc/spec/llvm_libc_ext.td b/libc/spec/llvm_libc_ext.td
index 86215029831ca..55b354cae74e0 100644
--- a/libc/spec/llvm_libc_ext.td
+++ b/libc/spec/llvm_libc_ext.td
@@ -65,6 +65,14 @@ def LLVMLibcExt : StandardSpec<"llvm_libc_ext"> {
           GuardedFunctionSpec<"f16subf", RetValSpec<Float16Type>, [ArgSpec<FloatType>, ArgSpec<FloatType>], "LIBC_TYPES_HAS_FLOAT16">,
           GuardedFunctionSpec<"f16subl", RetValSpec<Float16Type>, [ArgSpec<LongDoubleType>, ArgSpec<LongDoubleType>], "LIBC_TYPES_HAS_FLOAT16">,
 
+          GuardedFunctionSpec<"fmulf128", RetValSpec<FloatType>, [ArgSpec<Float128Type>, ArgSpec<Float128Type>], "LIBC_TYPES_HAS_FLOAT128">,
+
+          GuardedFunctionSpec<"dmulf128", RetValSpec<DoubleType>, [ArgSpec<Float128Type>, ArgSpec<Float128Type>], "LIBC_TYPES_HAS_FLOAT128">,
+
+          GuardedFunctionSpec<"f16mul", RetValSpec<Float16Type>, [ArgSpec<DoubleType>, ArgSpec<DoubleType>], "LIBC_TYPES_HAS_FLOAT16">,
+          GuardedFunctionSpec<"f16mulf", RetValSpec<Float16Type>, [ArgSpec<FloatType>, ArgSpec<FloatType>], "LIBC_TYPES_HAS_FLOAT16">,
+          GuardedFunctionSpec<"f16mull", RetValSpec<Float16Type>, [ArgSpec<LongDoubleType>, ArgSpec<LongDoubleType>], "LIBC_TYPES_HAS_FLOAT16">,
+
           GuardedFunctionSpec<"f16div", RetValSpec<Float16Type>, [ArgSpec<DoubleType>, ArgSpec<DoubleType>], "LIBC_TYPES_HAS_FLOAT16">,
           GuardedFunctionSpec<"f16divf", RetValSpec<Float16Type>, [ArgSpec<FloatType>, ArgSpec<FloatType>], "LIBC_TYPES_HAS_FLOAT16">,
           GuardedFunctionSpec<"f16divl", RetValSpec<Float16Type>, [ArgSpec<LongDoubleType>, ArgSpec<LongDoubleType>], "LIBC_TYPES_HAS_FLOAT16">,
diff --git a/libc/spec/stdc.td b/libc/spec/stdc.td
index aa56152aee141..2f1ca984616d3 100644
--- a/libc/spec/stdc.td
+++ b/libc/spec/stdc.td
@@ -474,8 +474,6 @@ def StdC : StandardSpec<"stdc"> {
           GuardedFunctionSpec<"fminimum_mag_numf16", RetValSpec<Float16Type>, [ArgSpec<Float16Type>, ArgSpec<Float16Type>], "LIBC_TYPES_HAS_FLOAT16">,
           GuardedFunctionSpec<"fminimum_mag_numf128", RetValSpec<Float128Type>, [ArgSpec<Float128Type>, ArgSpec<Float128Type>], "LIBC_TYPES_HAS_FLOAT128">,
 
-          FunctionSpec<"fmul", RetValSpec<FloatType>, [ArgSpec<DoubleType>, ArgSpec<DoubleType>]>,
-
           FunctionSpec<"fma", RetValSpec<DoubleType>, [ArgSpec<DoubleType>, ArgSpec<DoubleType>, ArgSpec<DoubleType>]>,
           FunctionSpec<"fmaf", RetValSpec<FloatType>, [ArgSpec<FloatType>, ArgSpec<FloatType>, ArgSpec<FloatType>]>,
 
@@ -732,6 +730,13 @@ def StdC : StandardSpec<"stdc"> {
 
           GuardedFunctionSpec<"f16subf128", RetValSpec<Float16Type>, [ArgSpec<Float128Type>, ArgSpec<Float128Type>], "LIBC_TYPES_HAS_FLOAT16_AND_FLOAT128">,
 
+          FunctionSpec<"fmul", RetValSpec<FloatType>, [ArgSpec<DoubleType>, ArgSpec<DoubleType>]>,
+          FunctionSpec<"fmull", RetValSpec<FloatType>, [ArgSpec<LongDoubleType>, ArgSpec<LongDoubleType>]>,
+
+          FunctionSpec<"dmull", RetValSpec<DoubleType>, [ArgSpec<LongDoubleType>, ArgSpec<LongDoubleType>]>,
+
+          GuardedFunctionSpec<"f16mulf128", RetValSpec<Float16Type>, [ArgSpec<Float128Type>, ArgSpec<Float128Type>], "LIBC_TYPES_HAS_FLOAT16_AND_FLOAT128">,
+
           GuardedFunctionSpec<"f16divf128", RetValSpec<Float16Type>, [ArgSpec<Float128Type>, ArgSpec<Float128Type>], "LIBC_TYPES_HAS_FLOAT16_AND_FLOAT128">,
 
           GuardedFunctionSpec<"f16sqrtf128", RetValSpec<Float16Type>, [ArgSpec<Float128Type>], "LIBC_TYPES_HAS_FLOAT16_AND_FLOAT128">,
diff --git a/libc/src/__support/FPUtil/generic/CMakeLists.txt b/libc/src/__support/FPUtil/generic/CMakeLists.txt
index c73f68723e232..43096aa529fc3 100644
--- a/libc/src/__support/FPUtil/generic/CMakeLists.txt
+++ b/libc/src/__support/FPUtil/generic/CMakeLists.txt
@@ -84,3 +84,20 @@ add_header_library(
     libc.src.__support.macros.attributes
     libc.src.__support.macros.optimization
 )
+
+add_header_library(
+  mul
+  HDRS
+    mul.h
+  DEPENDS
+    libc.hdr.errno_macros
+    libc.hdr.fenv_macros
+    libc.src.__support.CPP.bit
+    libc.src.__support.CPP.type_traits
+    libc.src.__support.FPUtil.basic_operations
+    libc.src.__support.FPUtil.fenv_impl
+    libc.src.__support.FPUtil.fp_bits
+    libc.src.__support.FPUtil.dyadic_float
+    libc.src.__support.macros.attributes
+    libc.src.__support.macros.optimization
+)
diff --git a/libc/src/__support/FPUtil/generic/mul.h b/libc/src/__support/FPUtil/generic/mul.h
new file mode 100644
index 0000000000000..636bb84d3a0db
--- /dev/null
+++ b/libc/src/__support/FPUtil/generic/mul.h
@@ -0,0 +1,120 @@
+//===-- Division of IEEE 754 floating-point numbers -------------*- 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_GENERIC_MUL_H
+#define LLVM_LIBC_SRC___SUPPORT_FPUTIL_GENERIC_MUL_H
+
+#include "hdr/errno_macros.h"
+#include "hdr/fenv_macros.h"
+#include "src/__support/CPP/bit.h"
+#include "src/__support/CPP/type_traits.h"
+#include "src/__support/FPUtil/BasicOperations.h"
+#include "src/__support/FPUtil/FEnvImpl.h"
+#include "src/__support/FPUtil/FPBits.h"
+#include "src/__support/FPUtil/dyadic_float.h"
+#include "src/__support/macros/attributes.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/macros/optimization.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace fputil::generic {
+
+template <typename OutType, typename InType>
+LIBC_INLINE cpp::enable_if_t<cpp::is_floating_point_v<OutType> &&
+                                 cpp::is_floating_point_v<InType> &&
+                                 sizeof(OutType) <= sizeof(InType),
+                             OutType>
+mul(InType x, InType y) {
+  using OutFPBits = FPBits<OutType>;
+  using OutStorageType = typename OutFPBits::StorageType;
+  using InFPBits = FPBits<InType>;
+  using InStorageType = typename InFPBits::StorageType;
+  // The product of two p-digit numbers is a 2p-digit number.
+  using DyadicFloat = DyadicFloat<cpp::bit_ceil(
+      2 * static_cast<size_t>(InFPBits::FRACTION_LEN))>;
+  using DyadicMantissaType = typename DyadicFloat::MantissaType;
+
+  InFPBits x_bits(x);
+  InFPBits y_bits(y);
+
+  Sign result_sign = x_bits.sign() == y_bits.sign() ? Sign::POS : Sign::NEG;
+
+  if (LIBC_UNLIKELY(x_bits.is_inf_or_nan() || y_bits.is_inf_or_nan() ||
+                    x_bits.is_zero() || y_bits.is_zero())) {
+    if (x_bits.is_nan() || y_bits.is_nan()) {
+      if (x_bits.is_signaling_nan() || y_bits.is_signaling_nan())
+        raise_except_if_required(FE_INVALID);
+
+      if (x_bits.is_quiet_nan()) {
+        InStorageType x_payload = static_cast<InStorageType>(getpayload(x));
+        if ((x_payload & ~(OutFPBits::FRACTION_MASK >> 1)) == 0)
+          return OutFPBits::quiet_nan(x_bits.sign(),
+                                      static_cast<OutStorageType>(x_payload))
+              .get_val();
+      }
+
+      if (y_bits.is_quiet_nan()) {
+        InStorageType y_payload = static_cast<InStorageType>(getpayload(y));
+        if ((y_payload & ~(OutFPBits::FRACTION_MASK >> 1)) == 0)
+          return OutFPBits::quiet_nan(y_bits.sign(),
+                                      static_cast<OutStorageType>(y_payload))
+              .get_val();
+      }
+
+      return OutFPBits::quiet_nan().get_val();
+    }
+
+    if (x_bits.is_inf()) {
+      if (y_bits.is_zero()) {
+        set_errno_if_required(EDOM);
+        raise_except_if_required(FE_INVALID);
+        return OutFPBits::quiet_nan().get_val();
+      }
+
+      return OutFPBits::inf(result_sign).get_val();
+    }
+
+    if (y_bits.is_inf()) {
+      if (x_bits.is_zero()) {
+        set_errno_if_required(EDOM);
+        raise_except_if_required(FE_INVALID);
+        return OutFPBits::quiet_nan().get_val();
+      }
+
+      return OutFPBits::inf(result_sign).get_val();
+    }
+
+    // Now either x or y is zero, and the other one is finite.
+    return OutFPBits::zero(result_sign).get_val();
+  }
+
+  DyadicFloat xd(x);
+  DyadicFloat yd(y);
+
+  // The product is either in the [2, 4) range or the [1, 2) range. We initially
+  // set the exponent as if the product were in the [2, 4) range. If the product
+  // is in the [1, 2) range, then it has 1 leading zero bit and the DyadicFloat
+  // constructor will normalize it and adjust the exponent.
+  int result_exp = xd.get_unbiased_exponent() + yd.get_unbiased_exponent() + 1 -
+                   DyadicMantissaType::BITS + 1;
+
+  // We use a single DyadicFloat type, which can fit the 2p-digit product, for
+  // both the normalized inputs and the product. So we need to right-shift the
+  // normalized input mantissas before multiplying them.
+  xd.shift_right(DyadicMantissaType::BITS / 2);
+  yd.shift_right(DyadicMantissaType::BITS / 2);
+  DyadicMantissaType product = xd.mantissa * yd.mantissa;
+
+  DyadicFloat result(result_sign, result_exp, product);
+  return result.template as<OutType, /*ShouldSignalExceptions=*/true>();
+}
+
+} // namespace fputil::generic
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_FPUTIL_GENERIC_MUL_H
diff --git a/libc/src/math/CMakeLists.txt b/libc/src/math/CMakeLists.txt
index 6462afbc54a4f..bd5e7f37c207b 100644
--- a/libc/src/math/CMakeLists.txt
+++ b/libc/src/math/CMakeLists.txt
@@ -85,6 +85,9 @@ add_math_entrypoint_object(cosh)
 add_math_entrypoint_object(coshf)
 add_math_entrypoint_object(cospif)
 
+add_math_entrypoint_object(dmull)
+add_math_entrypoint_object(dmulf128)
+
 add_math_entrypoint_object(erf)
 add_math_entrypoint_object(erff)
 
@@ -117,6 +120,11 @@ add_math_entrypoint_object(f16fmaf)
 add_math_entrypoint_object(f16fmal)
 add_math_entrypoint_object(f16fmaf128)
 
+add_math_entrypoint_object(f16mul)
+add_math_entrypoint_object(f16mulf)
+add_math_entrypoint_object(f16mull)
+add_math_entrypoint_object(f16mulf128)
+
 add_math_entrypoint_object(f16sqrt)
 add_math_entrypoint_object(f16sqrtf)
 add_math_entrypoint_object(f16sqrtl)
@@ -209,6 +217,8 @@ add_math_entrypoint_object(fminimum_mag_numf16)
 add_math_entrypoint_object(fminimum_mag_numf128)
 
 add_math_entrypoint_object(fmul)
+add_math_entrypoint_object(fmull)
+add_math_entrypoint_object(fmulf128)
 
 add_math_entrypoint_object(fmod)
 add_math_entrypoint_object(fmodf)
diff --git a/libc/src/math/dmulf128.h b/libc/src/math/dmulf128.h
new file mode 100644
index 0000000000000..623f22c910a41
--- /dev/null
+++ b/libc/src/math/dmulf128.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for dmulf128 ----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_MATH_DMULF128_H
+#define LLVM_LIBC_SRC_MATH_DMULF128_H
+
+#include "src/__support/macros/config.h"
+#include "src/__support/macros/properties/types.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+double dmulf128(float128 x, float128 y);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_MATH_DMULF128_H
diff --git a/libc/src/math/dmull.h b/libc/src/math/dmull.h
new file mode 100644
index 0000000000000..656776a603009
--- /dev/null
+++ b/libc/src/math/dmull.h
@@ -0,0 +1,20 @@
+//===-- Implementation header for dmull -------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_MATH_DMULL_H
+#define LLVM_LIBC_SRC_MATH_DMULL_H
+
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+double dmull(long double x, long double y);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_MATH_DMULL_H
diff --git a/libc/src/math/f16mul.h b/libc/src/math/f16mul.h
new file mode 100644
index 0000000000000..89403cf219271
--- /dev/null
+++ b/libc/src/math/f16mul.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for f16mul ------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_MATH_F16MUL_H
+#define LLVM_LIBC_SRC_MATH_F16MUL_H
+
+#include "src/__support/macros/config.h"
+#include "src/__support/macros/properties/types.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+float16 f16mul(double x, double y);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_MATH_F16MUL_H
diff --git a/libc/src/math/f16mulf.h b/libc/src/math/f16mulf.h
new file mode 100644
index 0000000000000..755886d6f14d0
--- /dev/null
+++ b/libc/src/math/f16mulf.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for f16mulf -----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_MATH_F16MULF_H
+#define LLVM_LIBC_SRC_MATH_F16MULF_H
+
+#include "src/__support/macros/config.h"
+#include "src/__support/macros/properties/types.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+float16 f16mulf(float x, float y);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_MATH_F16MULF_H
diff --git a/libc/src/math/f16mulf128.h b/libc/src/math/f16mulf128.h
new file mode 100644
index 0000000000000..14371c57ca88c
--- /dev/null
+++ b/libc/src/math/f16mulf128.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for f16mulf128 --------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_MATH_F16MULF128_H
+#define LLVM_LIBC_SRC_MATH_F16MULF128_H
+
+#include "src/__support/macros/config.h"
+#include "src/__support/macros/properties/types.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+float16 f16mulf128(float128 x, float128 y);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_MATH_F16MULF128_H
diff --git a/libc/src/math/f16mull.h b/libc/src/math/f16mull.h
new file mode 100644
index 0000000000000..a3177cadc1306
--- /dev/null
+++ b/libc/src/math/f16mull.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for f16mull -----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_MATH_F16MULL_H
+#define LLVM_LIBC_SRC_MATH_F16MULL_H
+
+#include "src/__support/macros/config.h"
+#include "src/__support/macros/properties/types.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+float16 f16mull(long double x, long double y);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_MATH_F16MULL_H
diff --git a/libc/src/math/fmulf128.h b/libc/src/math/fmulf128.h
new file mode 100644
index 0000000000000..94137ae87eb1e
--- /dev/null
+++ b/libc/src/math/fmulf128.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for fmulf128 ----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_MATH_FMULF128_H
+#define LLVM_LIBC_SRC_MATH_FMULF128_H
+
+#include "src/__support/macros/config.h"
+#include "src/__support/macros/properties/types.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+float fmulf128(float128 x, float128 y);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_MATH_FMULF128_H
diff --git a/libc/src/math/fmull.h b/libc/src/math/fmull.h
new file mode 100644
index 0000000000000..46e6c77cc66a2
--- /dev/null
+++ b/libc/src/math/fmull.h
@@ -0,0 +1,20 @@
+//===-- Implementation header for fmull -------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_MATH_FMULL_H
+#define LLVM_LIBC_SRC_MATH_FMULL_H
+
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+float fmull(long double x, long double y);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_MATH_FMULL_H
diff --git a/libc/src/math/generic/CMakeLists.txt b/libc/src/math/generic/CMakeLists.txt
index 5e920307d39de..41da91d23c2b0 100644
--- a/libc/src/math/generic/CMakeLists.txt
+++ b/libc/src/math/generic/CMakeLists.txt
@@ -2591,11 +2591,32 @@ add_entrypoint_object(
   HDRS
     ../fmul.h
   DEPENDS
-    libc.src.__support.FPUtil.basic_operations
-    libc.src.__support.uint128
-    libc.src.__support.CPP.bit
-    libc.src.__support.FPUtil.fp_bits
-    libc.src.__support.FPUtil.rounding_mode
+    libc.src.__support.FPUtil.generic.mul
+  COMPILE_OPTIONS
+    -O3
+)
+
+add_entrypoint_object(
+  fmull
+  SRCS
+    fmull.cpp
+  HDRS
+    ../fmull.h
+  DEPENDS
+    libc.src.__support.FPUtil.generic.mul
+  COMPILE_OPTIONS
+    -O3
+)
+
+add_entrypoint_object(
+  fmulf128
+  SRCS
+    fmulf128.cpp
+  HDRS
+    ../fmulf128.h
+  DEPENDS
+    libc.src.__support.macros.properties.types
+    libc.src.__support.FPUtil.generic.mul
   COMPILE_OPTIONS
     -O3
 )
@@ -4138,3 +4159,80 @@ add_entrypoint_object(
     libc.src.__support.FPUtil.multiply_add
     libc.src.__support.macros.optimization
 )
+
+add_entrypoint_object(
+  dmull
+  SRCS
+    dmull.cpp
+  HDRS
+    ../dmull.h
+  DEPENDS
+    libc.src.__support.FPUtil.generic.mul
+  COMPILE_OPTIONS
+    -O3
+)
+
+add_entrypoint_object(
+  dmulf128
+  SRCS
+    dmulf128.cpp
+  HDRS
+    ../dmulf128.h
+  DEPENDS
+    libc.src.__support.macros.properties.types
+    libc.src.__support.FPUtil.generic.mul
+  COMPILE_OPTIONS
+    -O3
+)
+
+add_entrypoint_object(
+  f16mul
+  SRCS
+    f16mul.cpp
+  HDRS
+    ../f16mul.h
+  DEPENDS
+    libc.src.__support.macros.properties.types
+    libc.src.__support.FPUtil.generic.mul
+  COMPILE_OPTIONS
+    -O3
+)
+
+add_entrypoint_object(
+  f16mulf
+  SRCS
+    f16mulf.cpp
+  HDRS
+    ../f16mulf.h
+  DEPENDS
+    libc.src.__support.macros.properties.types
+    libc.src.__support.FPUtil.generic.mul
+  COMPILE_OPTIONS
+    -O3
+)
+
+add_entrypoint_object(
+  f16mull
+  SRCS
+    f16mull.cpp
+  HDRS
+    ../f16mull.h
+  DEPENDS
+    libc.src.__support.macros.properties.types
+    libc.src.__support.FPUtil.generic.mul
+  COMPILE_OPTIONS
+    -O3
+)
+
+add_entrypoint_object(
+  f16mulf128
+  SRCS
+    f16mulf128.cpp
+  HDRS
+    ../f16mulf128.h
+  DEPENDS
+    libc.src.__support.macros.properties.types
+    libc.src.__support.FPUtil.generic.mul
+  COMPILE_OPTIONS
+    -O3
+)
diff --git a/libc/src/math/generic/dmulf128.cpp b/libc/src/math/generic/dmulf128.cpp
new file mode 100644
index 0000000000000..7e6ef95362c09
--- /dev/null
+++ b/libc/src/math/generic/dmulf128.cpp
@@ -0,0 +1,20 @@
+//===-- Implementation of dmulf128 function -------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/math/dmulf128.h"
+#include "src/__support/FPUtil/generic/mul.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(double, dmulf128, (float128 x, float128 y)) {
+  return fputil::generic::mul<double>(x, y);
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/math/generic/dmull.cpp b/libc/src/math/generic/dmull.cpp
new file mode 100644
index 0000000000000..428caa84a9977
--- /dev/null
+++ b/libc/src/math/generic/dmull.cpp
@@ -0,0 +1,20 @@
+//===-- Implementation of dmull function ----------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/math/dmull.h"
+#include "src/__support/FPUtil/generic/mul.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(double, dmull, (long double x, long double y)) {
+  return fputil::generic::mul<double>(x, y);
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/math/generic/f16mul.cpp b/libc/src/math/generic/f16mul.cpp
new file mode 100644
index 0000000000000..f7a5225b60f11
--- /dev/null
+++ b/libc/src/math/generic/f16mul.cpp
@@ -0,0 +1,20 @@
+//===-- Implementation of f16mul function ---------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/math/f16mul.h"
+#include "src/__support/FPUtil/generic/mul.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(float16, f16mul, (double x, double y)) {
+  return fputil::generic::mul<float16>(x, y);
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/math/generic/f16mulf.cpp b/libc/src/math/generic/f16mulf.cpp
new file mode 100644
index 0000000000000..2c04664f804ea
--- /dev/null
+++ b/libc/src/math/generic/f16mulf.cpp
@@ -0,0 +1,20 @@
+//===-- Implementation of f16mulf function --------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/math/f16mulf.h"
+#include "src/__support/FPUtil/generic/mul.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(float16, f16mulf, (float x, float y)) {
+  return fputil::generic::mul<float16>(x, y);
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/math/generic/f16mulf128.cpp b/libc/src/math/generic/f16mulf128.cpp
new file mode 100644
index 0000000000000..7e2d6a0d194ae
--- /dev/null
+++ b/libc/src/math/generic/f16mulf128.cpp
@@ -0,0 +1,20 @@
+//===-- Implementation of f16mulf128 function -----------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/math/f16mulf128.h"
+#include "src/__support/FPUtil/generic/mul.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(float16, f16mulf128, (float128 x, float128 y)) {
+  return fputil::generic::mul<float16>(x, y);
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/math/generic/f16mull.cpp b/libc/src/math/generic/f16mull.cpp
new file mode 100644
index 0000000000000..fc66fba4d9f23
--- /dev/null
+++ b/libc/src/math/generic/f16mull.cpp
@@ -0,0 +1,20 @@
+//===-- Implementation of f16mull function --------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/math/f16mull.h"
+#include "src/__support/FPUtil/generic/mul.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(float16, f16mull, (long double x, long double y)) {
+  return fputil::generic::mul<float16>(x, y);
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/math/generic/fmul.cpp b/libc/src/math/generic/fmul.cpp
index 16fa11e93df09..64c27d6e2f956 100644
--- a/libc/src/math/generic/fmul.cpp
+++ b/libc/src/math/generic/fmul.cpp
@@ -1,4 +1,4 @@
-//===-- Implementation of fmul function------------------------------------===//
+//===-- Implementation of fmul function -----------------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -7,123 +7,14 @@
 //===----------------------------------------------------------------------===//
 
 #include "src/math/fmul.h"
-#include "src/__support/CPP/bit.h"
-#include "src/__support/FPUtil/BasicOperations.h"
-#include "src/__support/FPUtil/FPBits.h"
-#include "src/__support/FPUtil/rounding_mode.h"
+#include "src/__support/FPUtil/generic/mul.h"
 #include "src/__support/common.h"
 #include "src/__support/macros/config.h"
-#include "src/__support/uint128.h"
 
 namespace LIBC_NAMESPACE_DECL {
 
 LLVM_LIBC_FUNCTION(float, fmul, (double x, double y)) {
-  auto x_bits = fputil::FPBits<double>(x);
-
-  auto y_bits = fputil::FPBits<double>(y);
-
-  auto output_sign = (x_bits.sign() != y_bits.sign()) ? Sign::NEG : Sign::POS;
-
-  if (LIBC_UNLIKELY(x_bits.is_inf_or_nan() || y_bits.is_inf_or_nan() ||
-                    x_bits.is_zero() || y_bits.is_zero())) {
-    if (x_bits.is_nan())
-      return static_cast<float>(x);
-    if (y_bits.is_nan())
-      return static_cast<float>(y);
-    if (x_bits.is_inf())
-      return y_bits.is_zero()
-                 ? fputil::FPBits<float>::quiet_nan().get_val()
-                 : fputil::FPBits<float>::inf(output_sign).get_val();
-    if (y_bits.is_inf())
-      return x_bits.is_zero()
-                 ? fputil::FPBits<float>::quiet_nan().get_val()
-                 : fputil::FPBits<float>::inf(output_sign).get_val();
-    // Now either x or y is zero, and the other one is finite.
-    return fputil::FPBits<float>::zero(output_sign).get_val();
-  }
-
-  uint64_t mx, my;
-
-  // Get mantissa and append the hidden bit if needed.
-  mx = x_bits.get_explicit_mantissa();
-  my = y_bits.get_explicit_mantissa();
-
-  // Get the corresponding biased exponent.
-  int ex = x_bits.get_explicit_exponent();
-  int ey = y_bits.get_explicit_exponent();
-
-  // Count the number of leading zeros of the explicit mantissas.
-  int nx = cpp::countl_zero(mx);
-  int ny = cpp::countl_zero(my);
-  // Shift the leading 1 bit to the most significant bit.
-  mx <<= nx;
-  my <<= ny;
-
-  // Adjust exponent accordingly: If x or y are normal, we will only need to
-  // shift by (exponent length + sign bit = 11 bits. If x or y are denormal, we
-  // will need to shift more than 11 bits.
-  ex -= (nx - 11);
-  ey -= (ny - 11);
-
-  UInt128 product = static_cast<UInt128>(mx) * static_cast<UInt128>(my);
-  int32_t dm1;
-  uint64_t highs, lows;
-  uint64_t g, hight, lowt;
-  uint32_t m;
-  uint32_t b;
-  int c;
-
-  highs = static_cast<uint64_t>(product >> 64);
-  c = static_cast<int>(highs >= 0x8000000000000000);
-  lows = static_cast<uint64_t>(product);
-
-  lowt = (lows != 0);
-
-  dm1 = ex + ey + c + fputil::FPBits<float>::EXP_BIAS;
-
-  int round_mode = fputil::quick_get_round();
-  if (dm1 >= 255) {
-    if ((round_mode == FE_TOWARDZERO) ||
-        (round_mode == FE_UPWARD && output_sign.is_neg()) ||
-        (round_mode == FE_DOWNWARD && output_sign.is_pos())) {
-      return fputil::FPBits<float>::max_normal(output_sign).get_val();
-    }
-    return fputil::FPBits<float>::inf().get_val();
-  } else if (dm1 <= 0) {
-
-    int m_shift = 40 + c - dm1;
-    int g_shift = m_shift - 1;
-    int h_shift = 64 - g_shift;
-    m = (m_shift >= 64) ? 0 : static_cast<uint32_t>(highs >> m_shift);
-
-    g = g_shift >= 64 ? 0 : (highs >> g_shift) & 1;
-    hight = h_shift >= 64 ? highs : (highs << h_shift) != 0;
-
-    dm1 = 0;
-  } else {
-    m = static_cast<uint32_t>(highs >> (39 + c));
-    g = (highs >> (38 + c)) & 1;
-    hight = (highs << (26 - c)) != 0;
-  }
-
-  if (round_mode == FE_TONEAREST) {
-    b = g && ((hight && lowt) || ((m & 1) != 0));
-  } else if ((output_sign.is_neg() && round_mode == FE_DOWNWARD) ||
-             (output_sign.is_pos() && round_mode == FE_UPWARD)) {
-    b = (g == 0 && (hight && lowt) == 0) ? 0 : 1;
-  } else {
-    b = 0;
-  }
-
-  uint32_t exp16 = (dm1 << 23);
-
-  uint32_t m2 = m & fputil::FPBits<float>::FRACTION_MASK;
-
-  uint32_t result = (exp16 + m2) + b;
-
-  auto result_bits = fputil::FPBits<float>(result);
-  result_bits.set_sign(output_sign);
-  return result_bits.get_val();
+  return fputil::generic::mul<float>(x, y);
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/math/generic/fmulf128.cpp b/libc/src/math/generic/fmulf128.cpp
new file mode 100644
index 0000000000000..c0c55ace641b8
--- /dev/null
+++ b/libc/src/math/generic/fmulf128.cpp
@@ -0,0 +1,20 @@
+//===-- Implementation of fmulf128 function -------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/math/fmulf128.h"
+#include "src/__support/FPUtil/generic/mul.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(float, fmulf128, (float128 x, float128 y)) {
+  return fputil::generic::mul<float>(x, y);
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/math/generic/fmull.cpp b/libc/src/math/generic/fmull.cpp
new file mode 100644
index 0000000000000..41ab165e7d09d
--- /dev/null
+++ b/libc/src/math/generic/fmull.cpp
@@ -0,0 +1,20 @@
+//===-- Implementation of fmull function ----------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/math/fmull.h"
+#include "src/__support/FPUtil/generic/mul.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(float, fmull, (long double x, long double y)) {
+  return fputil::generic::mul<float>(x, y);
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/test/src/math/CMakeLists.txt b/libc/test/src/math/CMakeLists.txt
index 0dc7ae6aae2df..e8d8ab77d11b1 100644
--- a/libc/test/src/math/CMakeLists.txt
+++ b/libc/test/src/math/CMakeLists.txt
@@ -1847,10 +1847,28 @@ add_fp_unittest(
   SRCS
     fmul_test.cpp
   HDRS
-    FMulTest.h
+    MulTest.h
   DEPENDS
     libc.src.math.fmul
+    libc.src.stdlib.rand
+    libc.src.stdlib.srand
 )
+
+add_fp_unittest(
+  fmull_test
+  NEED_MPFR
+  SUITE
+    libc-math-unittests
+  SRCS
+    fmull_test.cpp
+  HDRS
+    MulTest.h
+  DEPENDS
+    libc.src.math.fmull
+    libc.src.stdlib.rand
+    libc.src.stdlib.srand
+)
+
 add_fp_unittest(
   asinhf_test
   NEED_MPFR
@@ -2225,6 +2243,66 @@ add_fp_unittest(
     libc.src.__support.FPUtil.fp_bits
 )
 
+add_fp_unittest(
+  dmull_test
+  NEED_MPFR
+  SUITE
+    libc-math-unittests
+  SRCS
+    dmull_test.cpp
+  HDRS
+    MulTest.h
+  DEPENDS
+    libc.src.math.dmull
+    libc.src.stdlib.rand
+    libc.src.stdlib.srand
+)
+
+add_fp_unittest(
+  f16mul_test
+  NEED_MPFR
+  SUITE
+    libc-math-unittests
+  SRCS
+    f16mul_test.cpp
+  HDRS
+    MulTest.h
+  DEPENDS
+    libc.src.math.f16mul
+    libc.src.stdlib.rand
+    libc.src.stdlib.srand
+)
+
+add_fp_unittest(
+  f16mulf_test
+  NEED_MPFR
+  SUITE
+    libc-math-unittests
+  SRCS
+    f16mulf_test.cpp
+  HDRS
+    MulTest.h
+  DEPENDS
+    libc.src.math.f16mulf
+    libc.src.stdlib.rand
+    libc.src.stdlib.srand
+)
+
+add_fp_unittest(
+  f16mull_test
+  NEED_MPFR
+  SUITE
+    libc-math-unittests
+  SRCS
+    f16mull_test.cpp
+  HDRS
+    MulTest.h
+  DEPENDS
+    libc.src.math.f16mull
+    libc.src.stdlib.rand
+    libc.src.stdlib.srand
+)
+
 add_subdirectory(generic)
 add_subdirectory(smoke)
 
diff --git a/libc/test/src/math/FMulTest.h b/libc/test/src/math/FMulTest.h
deleted file mode 100644
index 8ca33ea71b712..0000000000000
--- a/libc/test/src/math/FMulTest.h
+++ /dev/null
@@ -1,121 +0,0 @@
-//===-- Utility class to test fmul[f|l] -------------------------*- C++ -*-===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_LIBC_TEST_SRC_MATH_FMULTEST_H
-#define LLVM_LIBC_TEST_SRC_MATH_FMULTEST_H
-
-#include "src/__support/FPUtil/FPBits.h"
-#include "test/UnitTest/FEnvSafeTest.h"
-#include "test/UnitTest/FPMatcher.h"
-#include "test/UnitTest/Test.h"
-#include "utils/MPFRWrapper/MPFRUtils.h"
-
-namespace mpfr = LIBC_NAMESPACE::testing::mpfr;
-
-template <typename OutType, typename InType>
-class FmulMPFRTest : public LIBC_NAMESPACE::testing::FEnvSafeTest {
-
-  DECLARE_SPECIAL_CONSTANTS(InType)
-
-public:
-  typedef OutType (*FMulFunc)(InType, InType);
-
-  void testFMulMPFR(FMulFunc func) {
-    constexpr int N = 10;
-    mpfr::BinaryInput<InType> INPUTS[N] = {
-        {3.0, 5.0},
-        {0x1.0p1, 0x1.0p-131},
-        {0x1.0p2, 0x1.0p-129},
-        {1.0, 1.0},
-        {-0.0, -0.0},
-        {-0.0, 0.0},
-        {0.0, -0.0},
-        {0x1.0p100, 0x1.0p100},
-        {1.0, 1.0 + 0x1.0p-128 + 0x1.0p-149 + 0x1.0p-150},
-        {1.0, 0x1.0p-128 + 0x1.0p-149 + 0x1.0p-150}};
-
-    for (int i = 0; i < N; ++i) {
-      InType x = INPUTS[i].x;
-      InType y = INPUTS[i].y;
-      ASSERT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Fmul, INPUTS[i],
-                                     func(x, y), 0.5);
-    }
-  }
-
-  void testSpecialInputsMPFR(FMulFunc func) {
-    constexpr int N = 27;
-    mpfr::BinaryInput<InType> INPUTS[N] = {{inf, 0x1.0p-129},
-                                           {0x1.0p-129, inf},
-                                           {inf, 2.0},
-                                           {3.0, inf},
-                                           {0.0, 0.0},
-                                           {neg_inf, aNaN},
-                                           {aNaN, neg_inf},
-                                           {neg_inf, neg_inf},
-                                           {0.0, neg_inf},
-                                           {neg_inf, 0.0},
-                                           {neg_inf, 1.0},
-                                           {1.0, neg_inf},
-                                           {neg_inf, 0x1.0p-129},
-                                           {0x1.0p-129, neg_inf},
-                                           {0.0, 0x1.0p-129},
-                                           {inf, 0.0},
-                                           {0.0, inf},
-                                           {0.0, aNaN},
-                                           {2.0, aNaN},
-                                           {0x1.0p-129, aNaN},
-                                           {inf, aNaN},
-                                           {aNaN, aNaN},
-                                           {0.0, sNaN},
-                                           {2.0, sNaN},
-                                           {0x1.0p-129, sNaN},
-                                           {inf, sNaN},
-                                           {sNaN, sNaN}};
-
-    for (int i = 0; i < N; ++i) {
-      InType x = INPUTS[i].x;
-      InType y = INPUTS[i].y;
-      ASSERT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Fmul, INPUTS[i],
-                                     func(x, y), 0.5);
-    }
-  }
-
-  void testNormalRange(FMulFunc func) {
-    using FPBits = LIBC_NAMESPACE::fputil::FPBits<InType>;
-    using StorageType = typename FPBits::StorageType;
-    static constexpr StorageType MAX_NORMAL = FPBits::max_normal().uintval();
-    static constexpr StorageType MIN_NORMAL = FPBits::min_normal().uintval();
-
-    constexpr StorageType COUNT = 10'001;
-    constexpr StorageType STEP = (MAX_NORMAL - MIN_NORMAL) / COUNT;
-    for (int signs = 0; signs < 4; ++signs) {
-      for (StorageType v = MIN_NORMAL, w = MAX_NORMAL;
-           v <= MAX_NORMAL && w >= MIN_NORMAL; v += STEP, w -= STEP) {
-        InType x = FPBits(v).get_val(), y = FPBits(w).get_val();
-        if (signs % 2 == 1) {
-          x = -x;
-        }
-        if (signs >= 2) {
-          y = -y;
-        }
-
-        mpfr::BinaryInput<InType> input{x, y};
-        ASSERT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Fmul, input, func(x, y),
-                                       0.5);
-      }
-    }
-  }
-};
-
-#define LIST_FMUL_MPFR_TESTS(OutType, InType, func)                            \
-  using LlvmLibcFmulTest = FmulMPFRTest<OutType, InType>;                      \
-  TEST_F(LlvmLibcFmulTest, MulMpfr) { testFMulMPFR(&func); }                   \
-  TEST_F(LlvmLibcFmulTest, NanInfMpfr) { testSpecialInputsMPFR(&func); }       \
-  TEST_F(LlvmLibcFmulTest, NormalRange) { testNormalRange(&func); }
-
-#endif // LLVM_LIBC_TEST_SRC_MATH_FMULTEST_H
diff --git a/libc/test/src/math/MulTest.h b/libc/test/src/math/MulTest.h
new file mode 100644
index 0000000000000..cb81a795be36b
--- /dev/null
+++ b/libc/test/src/math/MulTest.h
@@ -0,0 +1,95 @@
+//===-- Utility class to test different flavors of float mul ----*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_TEST_SRC_MATH_MULTEST_H
+#define LLVM_LIBC_TEST_SRC_MATH_MULTEST_H
+
+#include "src/stdlib/rand.h"
+#include "src/stdlib/srand.h"
+#include "test/UnitTest/FEnvSafeTest.h"
+#include "test/UnitTest/FPMatcher.h"
+#include "test/UnitTest/Test.h"
+#include "utils/MPFRWrapper/MPFRUtils.h"
+
+namespace mpfr = LIBC_NAMESPACE::testing::mpfr;
+
+template <typename OutType, typename InType>
+class MulTest : public LIBC_NAMESPACE::testing::FEnvSafeTest {
+
+  struct InConstants {
+    DECLARE_SPECIAL_CONSTANTS(InType)
+  };
+
+  using InFPBits = typename InConstants::FPBits;
+  using InStorageType = typename InConstants::StorageType;
+
+  static constexpr InStorageType IN_MAX_NORMAL_U =
+      InFPBits::max_normal().uintval();
+  static constexpr InStorageType IN_MIN_NORMAL_U =
+      InFPBits::min_normal().uintval();
+  static constexpr InStorageType IN_MAX_SUBNORMAL_U =
+      InFPBits::max_subnormal().uintval();
+  static constexpr InStorageType IN_MIN_SUBNORMAL_U =
+      InFPBits::min_subnormal().uintval();
+
+  InStorageType get_random_bit_pattern() {
+    InStorageType bits{0};
+    for (InStorageType i = 0; i < sizeof(InStorageType) / 2; ++i)
+      bits = (bits << 2) + static_cast<uint16_t>(LIBC_NAMESPACE::rand());
+    return bits;
+  }
+
+public:
+  using MulFunc = OutType (*)(InType, InType);
+
+  void test_subnormal_range(MulFunc func) {
+    constexpr InStorageType COUNT = 10'001;
+    constexpr InStorageType STEP =
+        (IN_MAX_SUBNORMAL_U - IN_MIN_SUBNORMAL_U) / COUNT;
+    LIBC_NAMESPACE::srand(1);
+    for (int signs = 0; signs < 4; signs++) {
+      for (InStorageType i = 0, v = 0; i <= COUNT; ++i, v += STEP) {
+        InType x = InFPBits(get_random_bit_pattern()).get_val();
+        InType y = InFPBits(v).get_val();
+        if ((signs & 1) != 0)
+          x = -x;
+        if ((signs & 2) != 0)
+          y = -y;
+        mpfr::BinaryInput<InType> input{x, y};
+        EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Mul, input, func(x, y),
+                                       0.5);
+      }
+    }
+  }
+
+  void test_normal_range(MulFunc func) {
+    constexpr InStorageType COUNT = 10'001;
+    constexpr InStorageType STEP = (IN_MAX_NORMAL_U - IN_MIN_NORMAL_U) / COUNT;
+    LIBC_NAMESPACE::srand(1);
+    for (int signs = 0; signs < 4; signs++) {
+      for (InStorageType i = 0, v = 0; i <= COUNT; ++i, v += STEP) {
+        InType x = InFPBits(get_random_bit_pattern()).get_val();
+        InType y = InFPBits(v).get_val();
+        if ((signs & 1) != 0)
+          x = -x;
+        if ((signs & 2) != 0)
+          y = -y;
+        mpfr::BinaryInput<InType> input{x, y};
+        EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Mul, input, func(x, y),
+                                       0.5);
+      }
+    }
+  }
+};
+
+#define LIST_MUL_TESTS(OutType, InType, func)                                  \
+  using LlvmLibcMulTest = MulTest<OutType, InType>;                            \
+  TEST_F(LlvmLibcMulTest, SubnormalRange) { test_subnormal_range(&func); }     \
+  TEST_F(LlvmLibcMulTest, NormalRange) { test_normal_range(&func); }
+
+#endif // LLVM_LIBC_TEST_SRC_MATH_MULTEST_H
diff --git a/libc/test/src/math/dmull_test.cpp b/libc/test/src/math/dmull_test.cpp
new file mode 100644
index 0000000000000..1b9c9c2c24ed3
--- /dev/null
+++ b/libc/test/src/math/dmull_test.cpp
@@ -0,0 +1,13 @@
+//===-- Unittests for dmull -----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MulTest.h"
+
+#include "src/math/dmull.h"
+
+LIST_MUL_TESTS(double, long double, LIBC_NAMESPACE::dmull)
diff --git a/libc/test/src/math/f16mul_test.cpp b/libc/test/src/math/f16mul_test.cpp
new file mode 100644
index 0000000000000..49b443870c483
--- /dev/null
+++ b/libc/test/src/math/f16mul_test.cpp
@@ -0,0 +1,13 @@
+//===-- Unittests for f16mul ----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MulTest.h"
+
+#include "src/math/f16mul.h"
+
+LIST_MUL_TESTS(float16, double, LIBC_NAMESPACE::f16mul)
diff --git a/libc/test/src/math/f16mulf_test.cpp b/libc/test/src/math/f16mulf_test.cpp
new file mode 100644
index 0000000000000..bf2530863621d
--- /dev/null
+++ b/libc/test/src/math/f16mulf_test.cpp
@@ -0,0 +1,13 @@
+//===-- Unittests for f16mulf ---------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MulTest.h"
+
+#include "src/math/f16mulf.h"
+
+LIST_MUL_TESTS(float16, float, LIBC_NAMESPACE::f16mulf)
diff --git a/libc/test/src/math/f16mull_test.cpp b/libc/test/src/math/f16mull_test.cpp
new file mode 100644
index 0000000000000..5292ddb87b7f4
--- /dev/null
+++ b/libc/test/src/math/f16mull_test.cpp
@@ -0,0 +1,13 @@
+//===-- Unittests for f16mull ---------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MulTest.h"
+
+#include "src/math/f16mull.h"
+
+LIST_MUL_TESTS(float16, long double, LIBC_NAMESPACE::f16mull)
diff --git a/libc/test/src/math/fmul_test.cpp b/libc/test/src/math/fmul_test.cpp
index 16eaa1a818daf..3f6df66456bac 100644
--- a/libc/test/src/math/fmul_test.cpp
+++ b/libc/test/src/math/fmul_test.cpp
@@ -1,13 +1,13 @@
-//===-- Unittests for fmul-------------------------------------------------===//
+//===-- Unittests for fmul ------------------------------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
-//===---------------------------------------------------------------------===//
+//===----------------------------------------------------------------------===//
 
-#include "FMulTest.h"
+#include "MulTest.h"
 
 #include "src/math/fmul.h"
 
-LIST_FMUL_MPFR_TESTS(float, double, LIBC_NAMESPACE::fmul)
+LIST_MUL_TESTS(float, double, LIBC_NAMESPACE::fmul)
diff --git a/libc/test/src/math/fmull_test.cpp b/libc/test/src/math/fmull_test.cpp
new file mode 100644
index 0000000000000..ef694063f20dc
--- /dev/null
+++ b/libc/test/src/math/fmull_test.cpp
@@ -0,0 +1,13 @@
+//===-- Unittests for fmull -----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MulTest.h"
+
+#include "src/math/fmull.h"
+
+LIST_MUL_TESTS(float, long double, LIBC_NAMESPACE::fmull)
diff --git a/libc/test/src/math/smoke/CMakeLists.txt b/libc/test/src/math/smoke/CMakeLists.txt
index 7f1bc0c204c68..895786ce01ada 100644
--- a/libc/test/src/math/smoke/CMakeLists.txt
+++ b/libc/test/src/math/smoke/CMakeLists.txt
@@ -2531,10 +2531,27 @@ add_fp_unittest(
   SRCS
     fmul_test.cpp
   HDRS
-    FMulTest.h
+    MulTest.h
   DEPENDS
+    libc.hdr.errno_macros
+    libc.hdr.fenv_macros
+    libc.src.__support.FPUtil.basic_operations
     libc.src.math.fmul
-    libc.src.__support.FPUtil.fp_bits
+)
+
+add_fp_unittest(
+  fmull_test
+  SUITE
+    libc-math-smoke-tests
+  SRCS
+    fmull_test.cpp
+  HDRS
+    MulTest.h
+  DEPENDS
+    libc.hdr.errno_macros
+    libc.hdr.fenv_macros
+    libc.src.__support.FPUtil.basic_operations
+    libc.src.math.fmull
 )
 
 add_fp_unittest(
@@ -3971,3 +3988,78 @@ add_fp_unittest(
   DEPENDS
     libc.src.math.cbrtf
 )
+
+add_fp_unittest(
+  dmull_test
+  SUITE
+    libc-math-smoke-tests
+  SRCS
+    dmull_test.cpp
+  HDRS
+    MulTest.h
+  DEPENDS
+    libc.hdr.errno_macros
+    libc.hdr.fenv_macros
+    libc.src.__support.FPUtil.basic_operations
+    libc.src.math.dmull
+)
+
+add_fp_unittest(
+  f16mul_test
+  SUITE
+    libc-math-smoke-tests
+  SRCS
+    f16mul_test.cpp
+  HDRS
+    MulTest.h
+  DEPENDS
+    libc.hdr.errno_macros
+    libc.hdr.fenv_macros
+    libc.src.__support.FPUtil.basic_operations
+    libc.src.math.f16mul
+)
+
+add_fp_unittest(
+  f16mulf_test
+  SUITE
+    libc-math-smoke-tests
+  SRCS
+    f16mulf_test.cpp
+  HDRS
+    MulTest.h
+  DEPENDS
+    libc.hdr.errno_macros
+    libc.hdr.fenv_macros
+    libc.src.__support.FPUtil.basic_operations
+    libc.src.math.f16mulf
+)
+
+add_fp_unittest(
+  f16mull_test
+  SUITE
+    libc-math-smoke-tests
+  SRCS
+    f16mull_test.cpp
+  HDRS
+    MulTest.h
+  DEPENDS
+    libc.hdr.errno_macros
+    libc.hdr.fenv_macros
+    libc.src.__support.FPUtil.basic_operations
+    libc.src.math.f16mull
+)
+
+add_fp_unittest(
+  f16mulf128_test
+  SUITE
+    libc-math-smoke-tests
+  SRCS
+    f16mulf128_test.cpp
+  HDRS
+    MulTest.h
+  DEPENDS
+    libc.hdr.errno_macros
+    libc.hdr.fenv_macros
+    libc.src.__support.FPUtil.basic_operations
+    libc.src.math.f16mulf128
+)
diff --git a/libc/test/src/math/smoke/FMulTest.h b/libc/test/src/math/smoke/FMulTest.h
deleted file mode 100644
index 33fb82c8d2da1..0000000000000
--- a/libc/test/src/math/smoke/FMulTest.h
+++ /dev/null
@@ -1,104 +0,0 @@
-//===-- Utility class to test fmul[f|l] ---------------------*- C++ -*-===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_LIBC_TEST_SRC_MATH_SMOKE_FMULTEST_H
-#define LLVM_LIBC_TEST_SRC_MATH_SMOKE_FMULTEST_H
-
-#include "test/UnitTest/FEnvSafeTest.h"
-#include "test/UnitTest/FPMatcher.h"
-#include "test/UnitTest/Test.h"
-
-template <typename T, typename R>
-class FmulTest : public LIBC_NAMESPACE::testing::FEnvSafeTest {
-
-  DECLARE_SPECIAL_CONSTANTS(T)
-
-public:
-  typedef T (*FMulFunc)(R, R);
-
-  void testMul(FMulFunc func) {
-
-    EXPECT_FP_EQ_ALL_ROUNDING(T(15.0), func(3.0, 5.0));
-    EXPECT_FP_EQ_ALL_ROUNDING(T(0x1.0p-130), func(0x1.0p1, 0x1.0p-131));
-    EXPECT_FP_EQ_ALL_ROUNDING(T(0x1.0p-127), func(0x1.0p2, 0x1.0p-129));
-    EXPECT_FP_EQ_ALL_ROUNDING(T(1.0), func(1.0, 1.0));
-
-    EXPECT_FP_EQ_ALL_ROUNDING(T(0.0), func(-0.0, -0.0));
-    EXPECT_FP_EQ_ALL_ROUNDING(T(-0.0), func(0.0, -0.0));
-    EXPECT_FP_EQ_ALL_ROUNDING(T(-0.0), func(-0.0, 0.0));
-
-    EXPECT_FP_EQ_ROUNDING_NEAREST(inf, func(0x1.0p100, 0x1.0p100));
-    EXPECT_FP_EQ_ROUNDING_UPWARD(inf, func(0x1.0p100, 0x1.0p100));
-    EXPECT_FP_EQ_ROUNDING_DOWNWARD(max_normal, func(0x1.0p100, 0x1.0p100));
-    EXPECT_FP_EQ_ROUNDING_TOWARD_ZERO(max_normal, func(0x1.0p100, 0x1.0p100));
-
-    EXPECT_FP_EQ_ROUNDING_NEAREST(
-        0x1p0, func(1.0, 1.0 + 0x1.0p-128 + 0x1.0p-149 + 0x1.0p-150));
-    EXPECT_FP_EQ_ROUNDING_DOWNWARD(
-        0x1p0, func(1.0, 1.0 + 0x1.0p-128 + 0x1.0p-149 + 0x1.0p-150));
-    EXPECT_FP_EQ_ROUNDING_TOWARD_ZERO(
-        0x1p0, func(1.0, 1.0 + 0x1.0p-128 + 0x1.0p-149 + 0x1.0p-150));
-    EXPECT_FP_EQ_ROUNDING_UPWARD(
-        0x1p0, func(1.0, 1.0 + 0x1.0p-128 + 0x1.0p-149 + 0x1.0p-150));
-
-    EXPECT_FP_EQ_ROUNDING_NEAREST(
-        0x1.0p-128f + 0x1.0p-148f,
-        func(1.0, 0x1.0p-128 + 0x1.0p-149 + 0x1.0p-150));
-    EXPECT_FP_EQ_ROUNDING_UPWARD(
-        0x1.0p-128f + 0x1.0p-148f,
-        func(1.0, 0x1.0p-128 + 0x1.0p-149 + 0x1.0p-150));
-    EXPECT_FP_EQ_ROUNDING_DOWNWARD(
-        0x1.0p-128f + 0x1.0p-149f,
-        func(1.0, 0x1.0p-128 + 0x1.0p-149 + 0x1.0p-150));
-    EXPECT_FP_EQ_ROUNDING_TOWARD_ZERO(
-        0x1.0p-128f + 0x1.0p-149f,
-        func(1.0, 0x1.0p-128 + 0x1.0p-149 + 0x1.0p-150));
-  }
-
-  void testSpecialInputs(FMulFunc func) {
-    EXPECT_FP_EQ_ALL_ROUNDING(inf, func(inf, 0x1.0p-129));
-    EXPECT_FP_EQ_ALL_ROUNDING(inf, func(0x1.0p-129, inf));
-    EXPECT_FP_EQ_ALL_ROUNDING(inf, func(inf, 2.0));
-    EXPECT_FP_EQ_ALL_ROUNDING(inf, func(3.0, inf));
-    EXPECT_FP_EQ_ALL_ROUNDING(0.0, func(0.0, 0.0));
-
-    EXPECT_FP_EQ_ALL_ROUNDING(aNaN, func(neg_inf, aNaN));
-    EXPECT_FP_EQ_ALL_ROUNDING(aNaN, func(aNaN, neg_inf));
-    EXPECT_FP_EQ_ALL_ROUNDING(inf, func(neg_inf, neg_inf));
-
-    EXPECT_FP_EQ_ALL_ROUNDING(aNaN, func(0.0, neg_inf));
-    EXPECT_FP_EQ_ALL_ROUNDING(aNaN, func(neg_inf, 0.0));
-
-    EXPECT_FP_EQ_ALL_ROUNDING(neg_inf, func(neg_inf, 1.0));
-    EXPECT_FP_EQ_ALL_ROUNDING(neg_inf, func(1.0, neg_inf));
-
-    EXPECT_FP_EQ_ALL_ROUNDING(neg_inf, func(neg_inf, 0x1.0p-129));
-    EXPECT_FP_EQ_ALL_ROUNDING(neg_inf, func(0x1.0p-129, neg_inf));
-
-    EXPECT_FP_EQ_ALL_ROUNDING(0.0, func(0.0, 0x1.0p-129));
-    EXPECT_FP_EQ_ALL_ROUNDING(aNaN, func(inf, 0.0));
-    EXPECT_FP_EQ_ALL_ROUNDING(aNaN, func(0.0, inf));
-    EXPECT_FP_EQ_ALL_ROUNDING(aNaN, func(0.0, aNaN));
-    EXPECT_FP_EQ_ALL_ROUNDING(aNaN, func(2.0, aNaN));
-    EXPECT_FP_EQ_ALL_ROUNDING(aNaN, func(0x1.0p-129, aNaN));
-    EXPECT_FP_EQ_ALL_ROUNDING(aNaN, func(inf, aNaN));
-    EXPECT_FP_EQ_ALL_ROUNDING(aNaN, func(aNaN, aNaN));
-    EXPECT_FP_EQ_ALL_ROUNDING(aNaN, func(0.0, sNaN));
-    EXPECT_FP_EQ_ALL_ROUNDING(aNaN, func(2.0, sNaN));
-    EXPECT_FP_EQ_ALL_ROUNDING(aNaN, func(0x1.0p-129, sNaN));
-    EXPECT_FP_EQ_ALL_ROUNDING(aNaN, func(inf, sNaN));
-    EXPECT_FP_EQ_ALL_ROUNDING(aNaN, func(sNaN, sNaN));
-  }
-};
-
-#define LIST_FMUL_TESTS(T, R, func)                                            \
-  using LlvmLibcFmulTest = FmulTest<T, R>;                                     \
-  TEST_F(LlvmLibcFmulTest, Mul) { testMul(&func); }                            \
-  TEST_F(LlvmLibcFmulTest, NaNInf) { testSpecialInputs(&func); }
-
-#endif // LLVM_LIBC_TEST_SRC_MATH_SMOKE_FMULTEST_H
diff --git a/libc/test/src/math/smoke/MulTest.h b/libc/test/src/math/smoke/MulTest.h
new file mode 100644
index 0000000000000..e2298eaeeb216
--- /dev/null
+++ b/libc/test/src/math/smoke/MulTest.h
@@ -0,0 +1,171 @@
+//===-- Utility class to test different flavors of float mul ----*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_TEST_SRC_MATH_SMOKE_MULTEST_H
+#define LLVM_LIBC_TEST_SRC_MATH_SMOKE_MULTEST_H
+
+#include "hdr/errno_macros.h"
+#include "hdr/fenv_macros.h"
+#include "src/__support/FPUtil/BasicOperations.h"
+#include "test/UnitTest/FEnvSafeTest.h"
+#include "test/UnitTest/FPMatcher.h"
+#include "test/UnitTest/Test.h"
+
+template <typename OutType, typename InType>
+class MulTest : public LIBC_NAMESPACE::testing::FEnvSafeTest {
+
+  DECLARE_SPECIAL_CONSTANTS(OutType)
+
+  struct InConstants {
+    DECLARE_SPECIAL_CONSTANTS(InType)
+  };
+
+  using InFPBits = typename InConstants::FPBits;
+  using InStorageType = typename InConstants::StorageType;
+
+  InConstants in;
+
+public:
+  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);
+
+    InType qnan_42 = InFPBits::quiet_nan(Sign::POS, 0x42).get_val();
+    EXPECT_FP_EQ(InType(0x42.0p+0),
+                 LIBC_NAMESPACE::fputil::getpayload(func(qnan_42, zero)));
+    EXPECT_FP_EQ(InType(0x42.0p+0),
+                 LIBC_NAMESPACE::fputil::getpayload(func(zero, qnan_42)));
+
+    if constexpr (sizeof(OutType) < sizeof(InType)) {
+      InStorageType max_payload = InFPBits::FRACTION_MASK >> 1;
+      InType qnan_max = InFPBits::quiet_nan(Sign::POS, max_payload).get_val();
+      EXPECT_FP_EQ(zero,
+                   LIBC_NAMESPACE::fputil::getpayload(func(qnan_max, zero)));
+      EXPECT_FP_EQ(zero,
+                   LIBC_NAMESPACE::fputil::getpayload(func(zero, qnan_max)));
+      EXPECT_FP_EQ(InType(0x42.0p+0),
+                   LIBC_NAMESPACE::fputil::getpayload(func(qnan_max, qnan_42)));
+      EXPECT_FP_EQ(InType(0x42.0p+0),
+                   LIBC_NAMESPACE::fputil::getpayload(func(qnan_42, qnan_max)));
+    }
+
+    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_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(OutType(1.0), func(1.0, 1.0));
+    EXPECT_FP_EQ_ALL_ROUNDING(OutType(15.0), func(3.0, 5.0));
+    EXPECT_FP_EQ_ALL_ROUNDING(OutType(0x1.0p-13), func(0x1.0p+1, 0x1.0p-14));
+    EXPECT_FP_EQ_ALL_ROUNDING(OutType(0x1.0p-10), func(0x1.0p+2, 0x1.0p-12));
+  }
+
+  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);
+  }
+
+  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),
+                                  FE_OVERFLOW | FE_INEXACT);
+      EXPECT_MATH_ERRNO(ERANGE);
+      EXPECT_FP_EQ_WITH_EXCEPTION(neg_inf, func(neg_max_normal, max_normal),
+                                  FE_OVERFLOW | FE_INEXACT);
+      EXPECT_MATH_ERRNO(ERANGE);
+
+      EXPECT_FP_EQ_WITH_EXCEPTION(zero, func(in.min_denormal, in.min_denormal),
+                                  FE_UNDERFLOW | FE_INEXACT);
+      EXPECT_MATH_ERRNO(ERANGE);
+      EXPECT_FP_EQ_WITH_EXCEPTION(neg_zero,
+                                  func(in.neg_min_denormal, in.min_denormal),
+                                  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, max_normal),
+                                  FE_OVERFLOW | FE_INEXACT);
+      EXPECT_FP_EQ_WITH_EXCEPTION(neg_max_normal,
+                                  func(neg_max_normal, max_normal),
+                                  FE_OVERFLOW | FE_INEXACT);
+
+      EXPECT_FP_EQ_WITH_EXCEPTION(zero, func(in.min_denormal, in.min_denormal),
+                                  FE_UNDERFLOW | FE_INEXACT);
+      EXPECT_MATH_ERRNO(ERANGE);
+      EXPECT_FP_EQ_WITH_EXCEPTION(neg_zero,
+                                  func(in.neg_min_denormal, in.min_denormal),
+                                  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, max_normal),
+                                  FE_OVERFLOW | FE_INEXACT);
+      EXPECT_FP_EQ_WITH_EXCEPTION(neg_inf, func(neg_max_normal, max_normal),
+                                  FE_OVERFLOW | FE_INEXACT);
+      EXPECT_MATH_ERRNO(ERANGE);
+
+      EXPECT_FP_EQ_WITH_EXCEPTION(zero, func(in.min_denormal, in.min_denormal),
+                                  FE_UNDERFLOW | FE_INEXACT);
+      EXPECT_MATH_ERRNO(ERANGE);
+      EXPECT_FP_EQ_WITH_EXCEPTION(neg_min_denormal,
+                                  func(in.neg_min_denormal, in.min_denormal),
+                                  FE_UNDERFLOW | FE_INEXACT);
+      EXPECT_MATH_ERRNO(ERANGE);
+    }
+
+    if (ForceRoundingMode r(RoundingMode::Upward); r.success) {
+      EXPECT_FP_EQ_WITH_EXCEPTION(inf, func(max_normal, max_normal),
+                                  FE_OVERFLOW | FE_INEXACT);
+      EXPECT_MATH_ERRNO(ERANGE);
+      EXPECT_FP_EQ_WITH_EXCEPTION(neg_max_normal,
+                                  func(neg_max_normal, max_normal),
+                                  FE_OVERFLOW | FE_INEXACT);
+
+      EXPECT_FP_EQ_WITH_EXCEPTION(min_denormal,
+                                  func(in.min_denormal, in.min_denormal),
+                                  FE_UNDERFLOW | FE_INEXACT);
+      EXPECT_MATH_ERRNO(ERANGE);
+      EXPECT_FP_EQ_WITH_EXCEPTION(neg_zero,
+                                  func(in.neg_min_denormal, in.min_denormal),
+                                  FE_UNDERFLOW | FE_INEXACT);
+      EXPECT_MATH_ERRNO(ERANGE);
+    }
+  }
+
+  void test_inexact_results(MulFunc func) {
+    InFPBits x_bits = InFPBits::one();
+    x_bits.set_mantissa(InFPBits::SIG_MASK);
+    InType x = x_bits.get_val();
+    func(x, x);
+    EXPECT_FP_EXCEPTION(FE_INEXACT);
+  }
+};
+
+#define LIST_MUL_TESTS(OutType, InType, func)                                  \
+  using LlvmLibcMulTest = MulTest<OutType, InType>;                            \
+  TEST_F(LlvmLibcMulTest, SpecialNumbers) { test_special_numbers(&func); }     \
+  TEST_F(LlvmLibcMulTest, InvalidOperations) {                                 \
+    test_invalid_operations(&func);                                            \
+  }                                                                            \
+  TEST_F(LlvmLibcMulTest, RangeErrors) { test_range_errors(&func); }           \
+  TEST_F(LlvmLibcMulTest, InexactResults) { test_inexact_results(&func); }
+
+#endif // LLVM_LIBC_TEST_SRC_MATH_SMOKE_MULTEST_H
diff --git a/libc/test/src/math/smoke/dmulf128_test.cpp b/libc/test/src/math/smoke/dmulf128_test.cpp
new file mode 100644
index 0000000000000..2ee2d10b9a19b
--- /dev/null
+++ b/libc/test/src/math/smoke/dmulf128_test.cpp
@@ -0,0 +1,13 @@
+//===-- Unittests for dmulf128 --------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MulTest.h"
+
+#include "src/math/dmulf128.h"
+
+LIST_MUL_TESTS(double, float128, LIBC_NAMESPACE::dmulf128)
diff --git a/libc/test/src/math/smoke/dmull_test.cpp b/libc/test/src/math/smoke/dmull_test.cpp
new file mode 100644
index 0000000000000..1b9c9c2c24ed3
--- /dev/null
+++ b/libc/test/src/math/smoke/dmull_test.cpp
@@ -0,0 +1,13 @@
+//===-- Unittests for dmull -----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MulTest.h"
+
+#include "src/math/dmull.h"
+
+LIST_MUL_TESTS(double, long double, LIBC_NAMESPACE::dmull)
diff --git a/libc/test/src/math/smoke/f16mul_test.cpp b/libc/test/src/math/smoke/f16mul_test.cpp
new file mode 100644
index 0000000000000..49b443870c483
--- /dev/null
+++ b/libc/test/src/math/smoke/f16mul_test.cpp
@@ -0,0 +1,13 @@
+//===-- Unittests for f16mul ----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MulTest.h"
+
+#include "src/math/f16mul.h"
+
+LIST_MUL_TESTS(float16, double, LIBC_NAMESPACE::f16mul)
diff --git a/libc/test/src/math/smoke/f16mulf128_test.cpp b/libc/test/src/math/smoke/f16mulf128_test.cpp
new file mode 100644
index 0000000000000..46e52cf068a7b
--- /dev/null
+++ b/libc/test/src/math/smoke/f16mulf128_test.cpp
@@ -0,0 +1,13 @@
+//===-- Unittests for f16mulf128 ------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MulTest.h"
+
+#include "src/math/f16mulf128.h"
+
+LIST_MUL_TESTS(float16, float128, LIBC_NAMESPACE::f16mulf128)
diff --git a/libc/test/src/math/smoke/f16mulf_test.cpp b/libc/test/src/math/smoke/f16mulf_test.cpp
new file mode 100644
index 0000000000000..bf2530863621d
--- /dev/null
+++ b/libc/test/src/math/smoke/f16mulf_test.cpp
@@ -0,0 +1,13 @@
+//===-- Unittests for f16mulf ---------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MulTest.h"
+
+#include "src/math/f16mulf.h"
+
+LIST_MUL_TESTS(float16, float, LIBC_NAMESPACE::f16mulf)
diff --git a/libc/test/src/math/smoke/f16mull_test.cpp b/libc/test/src/math/smoke/f16mull_test.cpp
new file mode 100644
index 0000000000000..5292ddb87b7f4
--- /dev/null
+++ b/libc/test/src/math/smoke/f16mull_test.cpp
@@ -0,0 +1,13 @@
+//===-- Unittests for f16mull ---------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MulTest.h"
+
+#include "src/math/f16mull.h"
+
+LIST_MUL_TESTS(float16, long double, LIBC_NAMESPACE::f16mull)
diff --git a/libc/test/src/math/smoke/fmul_test.cpp b/libc/test/src/math/smoke/fmul_test.cpp
index 0eb664f7411ee..3f6df66456bac 100644
--- a/libc/test/src/math/smoke/fmul_test.cpp
+++ b/libc/test/src/math/smoke/fmul_test.cpp
@@ -1,13 +1,13 @@
-//===-- Unittests for fmul-------------------------------------------------===//
+//===-- Unittests for fmul ------------------------------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
-//===---------------------------------------------------------------------===//
+//===----------------------------------------------------------------------===//
 
-#include "FMulTest.h"
+#include "MulTest.h"
 
 #include "src/math/fmul.h"
 
-LIST_FMUL_TESTS(float, double, LIBC_NAMESPACE::fmul)
+LIST_MUL_TESTS(float, double, LIBC_NAMESPACE::fmul)
diff --git a/libc/test/src/math/smoke/fmulf128_test.cpp b/libc/test/src/math/smoke/fmulf128_test.cpp
new file mode 100644
index 0000000000000..37c8d1cf9908d
--- /dev/null
+++ b/libc/test/src/math/smoke/fmulf128_test.cpp
@@ -0,0 +1,13 @@
+//===-- Unittests for fmulf128 --------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MulTest.h"
+
+#include "src/math/fmulf128.h"
+
+LIST_MUL_TESTS(float, float128, LIBC_NAMESPACE::fmulf128)
diff --git a/libc/test/src/math/smoke/fmull_test.cpp b/libc/test/src/math/smoke/fmull_test.cpp
new file mode 100644
index 0000000000000..ef694063f20dc
--- /dev/null
+++ b/libc/test/src/math/smoke/fmull_test.cpp
@@ -0,0 +1,13 @@
+//===-- Unittests for fmull -----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MulTest.h"
+
+#include "src/math/fmull.h"
+
+LIST_MUL_TESTS(float, long double, LIBC_NAMESPACE::fmull)
diff --git a/libc/utils/MPFRWrapper/MPFRUtils.cpp b/libc/utils/MPFRWrapper/MPFRUtils.cpp
index b67a9da40bd7b..bb63e0b9f4de0 100644
--- a/libc/utils/MPFRWrapper/MPFRUtils.cpp
+++ b/libc/utils/MPFRWrapper/MPFRUtils.cpp
@@ -539,7 +539,7 @@ class MPFRNumber {
     return result;
   }
 
-  MPFRNumber fmul(const MPFRNumber &b) {
+  MPFRNumber mul(const MPFRNumber &b) {
     MPFRNumber result(*this);
     mpfr_mul(result.value, value, b.value, mpfr_rounding);
     return result;
@@ -800,12 +800,12 @@ binary_operation_one_output(Operation op, InputType x, InputType y,
     return inputX.fmod(inputY);
   case Operation::Hypot:
     return inputX.hypot(inputY);
+  case Operation::Mul:
+    return inputX.mul(inputY);
   case Operation::Pow:
     return inputX.pow(inputY);
   case Operation::Sub:
     return inputX.sub(inputY);
-  case Operation::Fmul:
-    return inputX.fmul(inputY);
   default:
     __builtin_unreachable();
   }
@@ -1013,15 +1013,18 @@ void explain_binary_operation_one_output_error(
 template void
 explain_binary_operation_one_output_error(Operation, const BinaryInput<float> &,
                                           float, double, RoundingMode);
+template void explain_binary_operation_one_output_error(
+    Operation, const BinaryInput<double> &, float, double, RoundingMode);
 template void explain_binary_operation_one_output_error(
     Operation, const BinaryInput<double> &, double, double, RoundingMode);
+template void explain_binary_operation_one_output_error(
+    Operation, const BinaryInput<long double> &, float, double, RoundingMode);
+template void explain_binary_operation_one_output_error(
+    Operation, const BinaryInput<long double> &, double, double, RoundingMode);
 template void
 explain_binary_operation_one_output_error(Operation,
                                           const BinaryInput<long double> &,
                                           long double, double, RoundingMode);
-
-template void explain_binary_operation_one_output_error(
-    Operation, const BinaryInput<double> &, float, double, RoundingMode);
 #ifdef LIBC_TYPES_HAS_FLOAT16
 template void explain_binary_operation_one_output_error(
     Operation, const BinaryInput<float16> &, float16, double, RoundingMode);
@@ -1195,6 +1198,12 @@ template bool compare_binary_operation_one_output(Operation,
                                                   const BinaryInput<double> &,
                                                   double, double, RoundingMode);
 template bool
+compare_binary_operation_one_output(Operation, const BinaryInput<long double> &,
+                                    float, double, RoundingMode);
+template bool
+compare_binary_operation_one_output(Operation, const BinaryInput<long double> &,
+                                    double, double, RoundingMode);
+template bool
 compare_binary_operation_one_output(Operation, const BinaryInput<long double> &,
                                     long double, double, RoundingMode);
 
diff --git a/libc/utils/MPFRWrapper/MPFRUtils.h b/libc/utils/MPFRWrapper/MPFRUtils.h
index 28390af9ee6d8..8d51fa4e47726 100644
--- a/libc/utils/MPFRWrapper/MPFRUtils.h
+++ b/libc/utils/MPFRWrapper/MPFRUtils.h
@@ -79,9 +79,9 @@ enum class Operation : int {
   Div,
   Fmod,
   Hypot,
+  Mul,
   Pow,
   Sub,
-  Fmul,
   EndBinaryOperationsSingleOutput,
 
   // Operations which take two floating point numbers of the same type as

>From ee95eb99642eae22ac97912f0a82c7284126328b Mon Sep 17 00:00:00 2001
From: OverMighty <its.overmighty at gmail.com>
Date: Wed, 17 Jul 2024 12:04:15 +0200
Subject: [PATCH 2/2] fixup! [libc][math][c23] Add {f,d}mul{l,f128} and
 f16mul{,f,l,f128} C23 math functions

Switch to fputil::quick_mul.
---
 libc/src/__support/FPUtil/generic/mul.h | 23 ++++-------------------
 1 file changed, 4 insertions(+), 19 deletions(-)

diff --git a/libc/src/__support/FPUtil/generic/mul.h b/libc/src/__support/FPUtil/generic/mul.h
index 636bb84d3a0db..02fc69c6cb1ba 100644
--- a/libc/src/__support/FPUtil/generic/mul.h
+++ b/libc/src/__support/FPUtil/generic/mul.h
@@ -1,4 +1,4 @@
-//===-- Division of IEEE 754 floating-point numbers -------------*- C++ -*-===//
+//===-- Multiplication of IEEE 754 floating-point numbers -------*- C++ -*-===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -35,9 +35,8 @@ mul(InType x, InType y) {
   using InFPBits = FPBits<InType>;
   using InStorageType = typename InFPBits::StorageType;
   // The product of two p-digit numbers is a 2p-digit number.
-  using DyadicFloat = DyadicFloat<cpp::bit_ceil(
-      2 * static_cast<size_t>(InFPBits::FRACTION_LEN))>;
-  using DyadicMantissaType = typename DyadicFloat::MantissaType;
+  using DyadicFloat =
+      DyadicFloat<cpp::bit_ceil(2 * static_cast<size_t>(InFPBits::SIG_LEN))>;
 
   InFPBits x_bits(x);
   InFPBits y_bits(y);
@@ -96,21 +95,7 @@ mul(InType x, InType y) {
   DyadicFloat xd(x);
   DyadicFloat yd(y);
 
-  // The product is either in the [2, 4) range or the [1, 2) range. We initially
-  // set the exponent as if the product were in the [2, 4) range. If the product
-  // is in the [1, 2) range, then it has 1 leading zero bit and the DyadicFloat
-  // constructor will normalize it and adjust the exponent.
-  int result_exp = xd.get_unbiased_exponent() + yd.get_unbiased_exponent() + 1 -
-                   DyadicMantissaType::BITS + 1;
-
-  // We use a single DyadicFloat type, which can fit the 2p-digit product, for
-  // both the normalized inputs and the product. So we need to right-shift the
-  // normalized input mantissas before multiplying them.
-  xd.shift_right(DyadicMantissaType::BITS / 2);
-  yd.shift_right(DyadicMantissaType::BITS / 2);
-  DyadicMantissaType product = xd.mantissa * yd.mantissa;
-
-  DyadicFloat result(result_sign, result_exp, product);
+  DyadicFloat result = quick_mul(xd, yd);
   return result.template as<OutType, /*ShouldSignalExceptions=*/true>();
 }
 



More information about the libc-commits mailing list