[flang] Implement numeric intrinsic functions in runtime

Adds APIs, implementations, and unit tests for AINT, ANINT,
CEILING, EXPONENT, FLOOR, FRACTION, MOD, MODULO, NEAREST, NINT,
RRSPACING, SCALE, SET_EXPONENT, & SPACING.

Differential Revision: https://reviews.llvm.org/D99764

GitOrigin-RevId: 5f6c5c63c7c21cc9cf1c2b21759e40aa4854d496
diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt
index fc90432..49ac98d 100644
--- a/runtime/CMakeLists.txt
+++ b/runtime/CMakeLists.txt
@@ -51,6 +51,7 @@
   io-stmt.cpp
   main.cpp
   memory.cpp
+  numeric.cpp
   reduction.cpp
   stat.cpp
   stop.cpp
diff --git a/runtime/numeric.cpp b/runtime/numeric.cpp
new file mode 100644
index 0000000..a2d32d8
--- /dev/null
+++ b/runtime/numeric.cpp
@@ -0,0 +1,773 @@
+//===-- runtime/numeric.cpp -------------------------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "numeric.h"
+#include "flang/Common/long-double.h"
+#include <climits>
+#include <cmath>
+#include <limits>
+
+namespace Fortran::runtime {
+
+// AINT
+template <typename RESULT, typename ARG> inline RESULT Aint(ARG x) {
+  return std::trunc(x);
+}
+
+// ANINT & NINT
+template <typename RESULT, typename ARG> inline RESULT Anint(ARG x) {
+  if (x >= 0) {
+    return std::trunc(x + ARG{0.5});
+  } else {
+    return std::trunc(x - ARG{0.5});
+  }
+}
+
+// CEILING & FLOOR (16.9.43, .79)
+template <typename RESULT, typename ARG> inline RESULT Ceiling(ARG x) {
+  return std::ceil(x);
+}
+template <typename RESULT, typename ARG> inline RESULT Floor(ARG x) {
+  return std::floor(x);
+}
+
+// EXPONENT (16.9.75)
+template <typename RESULT, typename ARG> inline RESULT Exponent(ARG x) {
+  if (std::isinf(x) || std::isnan(x)) {
+    return std::numeric_limits<RESULT>::max(); // +/-Inf, NaN -> HUGE(0)
+  } else if (x == 0) {
+    return 0; // 0 -> 0
+  } else {
+    return std::ilogb(x) + 1;
+  }
+}
+
+// FRACTION (16.9.80)
+template <typename T> inline T Fraction(T x) {
+  if (std::isnan(x)) {
+    return x; // NaN -> same NaN
+  } else if (std::isinf(x)) {
+    return std::numeric_limits<T>::quiet_NaN(); // +/-Inf -> NaN
+  } else if (x == 0) {
+    return 0; // 0 -> 0
+  } else {
+    int ignoredExp;
+    return std::frexp(x, &ignoredExp);
+  }
+}
+
+// MOD & MODULO (16.9.135, .136)
+template <bool IS_MODULO, typename T> inline T IntMod(T x, T p) {
+  auto mod{x - (x / p) * p};
+  if (IS_MODULO && (x > 0) != (p > 0)) {
+    mod += p;
+  }
+  return mod;
+}
+template <bool IS_MODULO, typename T> inline T RealMod(T x, T p) {
+  if constexpr (IS_MODULO) {
+    return x - std::floor(x / p) * p;
+  } else {
+    return x - std::trunc(x / p) * p;
+  }
+}
+
+// RRSPACING (16.9.164)
+template <int PREC, typename T> inline T RRSpacing(T x) {
+  if (std::isnan(x)) {
+    return x; // NaN -> same NaN
+  } else if (std::isinf(x)) {
+    return std::numeric_limits<T>::quiet_NaN(); // +/-Inf -> NaN
+  } else if (x == 0) {
+    return 0; // 0 -> 0
+  } else {
+    return std::ldexp(std::abs(x), PREC - (std::ilogb(x) + 1));
+  }
+}
+
+// SCALE (16.9.166)
+template <typename T> inline T Scale(T x, std::int64_t p) {
+  auto ip{static_cast<int>(p)};
+  if (ip != p) {
+    ip = p < 0 ? std::numeric_limits<int>::min()
+               : std::numeric_limits<int>::max();
+  }
+  return std::ldexp(x, p); // x*2**p
+}
+
+// SET_EXPONENT (16.9.171)
+template <typename T> inline T SetExponent(T x, std::int64_t p) {
+  if (std::isnan(x)) {
+    return x; // NaN -> same NaN
+  } else if (std::isinf(x)) {
+    return std::numeric_limits<T>::quiet_NaN(); // +/-Inf -> NaN
+  } else if (x == 0) {
+    return 0; // 0 -> 0
+  } else {
+    int expo{std::ilogb(x)};
+    auto ip{static_cast<int>(p - expo)};
+    if (ip != p - expo) {
+      ip = p < 0 ? std::numeric_limits<int>::min()
+                 : std::numeric_limits<int>::max();
+    }
+    return std::ldexp(x, ip); // x*2**(p-e)
+  }
+}
+
+// SPACING (16.9.180)
+template <int PREC, typename T> inline T Spacing(T x) {
+  if (std::isnan(x)) {
+    return x; // NaN -> same NaN
+  } else if (std::isinf(x)) {
+    return std::numeric_limits<T>::quiet_NaN(); // +/-Inf -> NaN
+  } else if (x == 0) {
+    // The standard-mandated behavior seems broken, since TINY() can't be
+    // subnormal.
+    return std::numeric_limits<T>::min(); // 0 -> TINY(x)
+  } else {
+    return std::ldexp(
+        static_cast<T>(1.0), std::ilogb(x) + 1 - PREC); // 2**(e-p)
+  }
+}
+
+// NEAREST (16.9.139)
+template <int PREC, typename T> inline T Nearest(T x, bool positive) {
+  auto spacing{Spacing<PREC>(x)};
+  if (x == 0) {
+    auto least{std::numeric_limits<T>::denorm_min()};
+    return positive ? least : -least;
+  } else {
+    return positive ? x + spacing : x - spacing;
+  }
+}
+
+extern "C" {
+
+CppTypeFor<TypeCategory::Real, 4> RTNAME(Aint4_4)(
+    CppTypeFor<TypeCategory::Real, 4> x) {
+  return Aint<CppTypeFor<TypeCategory::Real, 4>>(x);
+}
+CppTypeFor<TypeCategory::Real, 8> RTNAME(Aint4_8)(
+    CppTypeFor<TypeCategory::Real, 4> x) {
+  return Aint<CppTypeFor<TypeCategory::Real, 8>>(x);
+}
+CppTypeFor<TypeCategory::Real, 4> RTNAME(Aint8_4)(
+    CppTypeFor<TypeCategory::Real, 8> x) {
+  return Aint<CppTypeFor<TypeCategory::Real, 4>>(x);
+}
+CppTypeFor<TypeCategory::Real, 8> RTNAME(Aint8_8)(
+    CppTypeFor<TypeCategory::Real, 8> x) {
+  return Aint<CppTypeFor<TypeCategory::Real, 8>>(x);
+}
+#if LONG_DOUBLE == 80
+CppTypeFor<TypeCategory::Real, 10> RTNAME(Aint4_10)(
+    CppTypeFor<TypeCategory::Real, 4> x) {
+  return Aint<CppTypeFor<TypeCategory::Real, 10>>(x);
+}
+CppTypeFor<TypeCategory::Real, 10> RTNAME(Aint8_10)(
+    CppTypeFor<TypeCategory::Real, 8> x) {
+  return Aint<CppTypeFor<TypeCategory::Real, 10>>(x);
+}
+CppTypeFor<TypeCategory::Real, 4> RTNAME(Aint10_4)(
+    CppTypeFor<TypeCategory::Real, 10> x) {
+  return Aint<CppTypeFor<TypeCategory::Real, 4>>(x);
+}
+CppTypeFor<TypeCategory::Real, 8> RTNAME(Aint10_8)(
+    CppTypeFor<TypeCategory::Real, 10> x) {
+  return Aint<CppTypeFor<TypeCategory::Real, 8>>(x);
+}
+CppTypeFor<TypeCategory::Real, 10> RTNAME(Aint10_10)(
+    CppTypeFor<TypeCategory::Real, 10> x) {
+  return Aint<CppTypeFor<TypeCategory::Real, 10>>(x);
+}
+#elif LONG_DOUBLE == 128
+CppTypeFor<TypeCategory::Real, 16> RTNAME(Aint4_16)(
+    CppTypeFor<TypeCategory::Real, 4> x) {
+  return Aint<CppTypeFor<TypeCategory::Real, 16>>(x);
+}
+CppTypeFor<TypeCategory::Real, 16> RTNAME(Aint8_16)(
+    CppTypeFor<TypeCategory::Real, 8> x) {
+  return Aint<CppTypeFor<TypeCategory::Real, 16>>(x);
+}
+CppTypeFor<TypeCategory::Real, 4> RTNAME(Aint16_4)(
+    CppTypeFor<TypeCategory::Real, 16> x) {
+  return Aint<CppTypeFor<TypeCategory::Real, 4>>(x);
+}
+CppTypeFor<TypeCategory::Real, 8> RTNAME(Aint16_8)(
+    CppTypeFor<TypeCategory::Real, 16> x) {
+  return Aint<CppTypeFor<TypeCategory::Real, 8>>(x);
+}
+CppTypeFor<TypeCategory::Real, 16> RTNAME(Aint16_16)(
+    CppTypeFor<TypeCategory::Real, 16> x) {
+  return Aint<CppTypeFor<TypeCategory::Real, 16>>(x);
+}
+#endif
+
+CppTypeFor<TypeCategory::Real, 4> RTNAME(Anint4_4)(
+    CppTypeFor<TypeCategory::Real, 4> x) {
+  return Anint<CppTypeFor<TypeCategory::Real, 4>>(x);
+}
+CppTypeFor<TypeCategory::Real, 8> RTNAME(Anint4_8)(
+    CppTypeFor<TypeCategory::Real, 4> x) {
+  return Anint<CppTypeFor<TypeCategory::Real, 8>>(x);
+}
+CppTypeFor<TypeCategory::Real, 4> RTNAME(Anint8_4)(
+    CppTypeFor<TypeCategory::Real, 8> x) {
+  return Anint<CppTypeFor<TypeCategory::Real, 4>>(x);
+}
+CppTypeFor<TypeCategory::Real, 8> RTNAME(Anint8_8)(
+    CppTypeFor<TypeCategory::Real, 8> x) {
+  return Anint<CppTypeFor<TypeCategory::Real, 8>>(x);
+}
+#if LONG_DOUBLE == 80
+CppTypeFor<TypeCategory::Real, 10> RTNAME(Anint4_10)(
+    CppTypeFor<TypeCategory::Real, 4> x) {
+  return Anint<CppTypeFor<TypeCategory::Real, 10>>(x);
+}
+CppTypeFor<TypeCategory::Real, 10> RTNAME(Anint8_10)(
+    CppTypeFor<TypeCategory::Real, 8> x) {
+  return Anint<CppTypeFor<TypeCategory::Real, 10>>(x);
+}
+CppTypeFor<TypeCategory::Real, 4> RTNAME(Anint10_4)(
+    CppTypeFor<TypeCategory::Real, 10> x) {
+  return Anint<CppTypeFor<TypeCategory::Real, 4>>(x);
+}
+CppTypeFor<TypeCategory::Real, 8> RTNAME(Anint10_8)(
+    CppTypeFor<TypeCategory::Real, 10> x) {
+  return Anint<CppTypeFor<TypeCategory::Real, 8>>(x);
+}
+CppTypeFor<TypeCategory::Real, 10> RTNAME(Anint10_10)(
+    CppTypeFor<TypeCategory::Real, 10> x) {
+  return Anint<CppTypeFor<TypeCategory::Real, 10>>(x);
+}
+#elif LONG_DOUBLE == 128
+CppTypeFor<TypeCategory::Real, 16> RTNAME(Anint4_16)(
+    CppTypeFor<TypeCategory::Real, 4> x) {
+  return Anint<CppTypeFor<TypeCategory::Real, 16>>(x);
+}
+CppTypeFor<TypeCategory::Real, 16> RTNAME(Anint8_16)(
+    CppTypeFor<TypeCategory::Real, 8> x) {
+  return Anint<CppTypeFor<TypeCategory::Real, 16>>(x);
+}
+CppTypeFor<TypeCategory::Real, 4> RTNAME(Anint16_4)(
+    CppTypeFor<TypeCategory::Real, 16> x) {
+  return Anint<CppTypeFor<TypeCategory::Real, 4>>(x);
+}
+CppTypeFor<TypeCategory::Real, 8> RTNAME(Anint16_8)(
+    CppTypeFor<TypeCategory::Real, 16> x) {
+  return Anint<CppTypeFor<TypeCategory::Real, 8>>(x);
+}
+CppTypeFor<TypeCategory::Real, 16> RTNAME(Anint16_16)(
+    CppTypeFor<TypeCategory::Real, 16> x) {
+  return Anint<CppTypeFor<TypeCategory::Real, 16>>(x);
+}
+#endif
+
+CppTypeFor<TypeCategory::Integer, 1> RTNAME(Ceiling4_1)(
+    CppTypeFor<TypeCategory::Real, 4> x) {
+  return Ceiling<CppTypeFor<TypeCategory::Integer, 1>>(x);
+}
+CppTypeFor<TypeCategory::Integer, 2> RTNAME(Ceiling4_2)(
+    CppTypeFor<TypeCategory::Real, 4> x) {
+  return Ceiling<CppTypeFor<TypeCategory::Integer, 2>>(x);
+}
+CppTypeFor<TypeCategory::Integer, 4> RTNAME(Ceiling4_4)(
+    CppTypeFor<TypeCategory::Real, 4> x) {
+  return Ceiling<CppTypeFor<TypeCategory::Integer, 4>>(x);
+}
+CppTypeFor<TypeCategory::Integer, 8> RTNAME(Ceiling4_8)(
+    CppTypeFor<TypeCategory::Real, 4> x) {
+  return Ceiling<CppTypeFor<TypeCategory::Integer, 8>>(x);
+}
+CppTypeFor<TypeCategory::Integer, 16> RTNAME(Ceiling4_16)(
+    CppTypeFor<TypeCategory::Real, 4> x) {
+  return Ceiling<CppTypeFor<TypeCategory::Integer, 16>>(x);
+}
+CppTypeFor<TypeCategory::Integer, 1> RTNAME(Ceiling8_1)(
+    CppTypeFor<TypeCategory::Real, 8> x) {
+  return Ceiling<CppTypeFor<TypeCategory::Integer, 1>>(x);
+}
+CppTypeFor<TypeCategory::Integer, 2> RTNAME(Ceiling8_2)(
+    CppTypeFor<TypeCategory::Real, 8> x) {
+  return Ceiling<CppTypeFor<TypeCategory::Integer, 2>>(x);
+}
+CppTypeFor<TypeCategory::Integer, 4> RTNAME(Ceiling8_4)(
+    CppTypeFor<TypeCategory::Real, 8> x) {
+  return Ceiling<CppTypeFor<TypeCategory::Integer, 4>>(x);
+}
+CppTypeFor<TypeCategory::Integer, 8> RTNAME(Ceiling8_8)(
+    CppTypeFor<TypeCategory::Real, 8> x) {
+  return Ceiling<CppTypeFor<TypeCategory::Integer, 8>>(x);
+}
+CppTypeFor<TypeCategory::Integer, 16> RTNAME(Ceiling8_16)(
+    CppTypeFor<TypeCategory::Real, 8> x) {
+  return Ceiling<CppTypeFor<TypeCategory::Integer, 16>>(x);
+}
+#if LONG_DOUBLE == 80
+CppTypeFor<TypeCategory::Integer, 1> RTNAME(Ceiling10_1)(
+    CppTypeFor<TypeCategory::Real, 10> x) {
+  return Ceiling<CppTypeFor<TypeCategory::Integer, 1>>(x);
+}
+CppTypeFor<TypeCategory::Integer, 2> RTNAME(Ceiling10_2)(
+    CppTypeFor<TypeCategory::Real, 10> x) {
+  return Ceiling<CppTypeFor<TypeCategory::Integer, 2>>(x);
+}
+CppTypeFor<TypeCategory::Integer, 4> RTNAME(Ceiling10_4)(
+    CppTypeFor<TypeCategory::Real, 10> x) {
+  return Ceiling<CppTypeFor<TypeCategory::Integer, 4>>(x);
+}
+CppTypeFor<TypeCategory::Integer, 8> RTNAME(Ceiling10_8)(
+    CppTypeFor<TypeCategory::Real, 10> x) {
+  return Ceiling<CppTypeFor<TypeCategory::Integer, 8>>(x);
+}
+CppTypeFor<TypeCategory::Integer, 16> RTNAME(Ceiling10_16)(
+    CppTypeFor<TypeCategory::Real, 10> x) {
+  return Ceiling<CppTypeFor<TypeCategory::Integer, 16>>(x);
+}
+#else
+CppTypeFor<TypeCategory::Integer, 1> RTNAME(Ceiling16_1)(
+    CppTypeFor<TypeCategory::Real, 16> x) {
+  return Ceiling<CppTypeFor<TypeCategory::Integer, 1>>(x);
+}
+CppTypeFor<TypeCategory::Integer, 2> RTNAME(Ceiling16_2)(
+    CppTypeFor<TypeCategory::Real, 16> x) {
+  return Ceiling<CppTypeFor<TypeCategory::Integer, 2>>(x);
+}
+CppTypeFor<TypeCategory::Integer, 4> RTNAME(Ceiling16_4)(
+    CppTypeFor<TypeCategory::Real, 16> x) {
+  return Ceiling<CppTypeFor<TypeCategory::Integer, 4>>(x);
+}
+CppTypeFor<TypeCategory::Integer, 8> RTNAME(Ceiling16_8)(
+    CppTypeFor<TypeCategory::Real, 16> x) {
+  return Ceiling<CppTypeFor<TypeCategory::Integer, 8>>(x);
+}
+CppTypeFor<TypeCategory::Integer, 16> RTNAME(Ceiling16_16)(
+    CppTypeFor<TypeCategory::Real, 16> x) {
+  return Ceiling<CppTypeFor<TypeCategory::Integer, 16>>(x);
+}
+#endif
+
+CppTypeFor<TypeCategory::Integer, 4> RTNAME(Exponent4_4)(
+    CppTypeFor<TypeCategory::Real, 4> x) {
+  return Exponent<CppTypeFor<TypeCategory::Integer, 4>>(x);
+}
+CppTypeFor<TypeCategory::Integer, 8> RTNAME(Exponent4_8)(
+    CppTypeFor<TypeCategory::Real, 4> x) {
+  return Exponent<CppTypeFor<TypeCategory::Integer, 8>>(x);
+}
+CppTypeFor<TypeCategory::Integer, 4> RTNAME(Exponent8_4)(
+    CppTypeFor<TypeCategory::Real, 8> x) {
+  return Exponent<CppTypeFor<TypeCategory::Integer, 4>>(x);
+}
+CppTypeFor<TypeCategory::Integer, 8> RTNAME(Exponent8_8)(
+    CppTypeFor<TypeCategory::Real, 8> x) {
+  return Exponent<CppTypeFor<TypeCategory::Integer, 8>>(x);
+}
+#if LONG_DOUBLE == 80
+CppTypeFor<TypeCategory::Integer, 4> RTNAME(Exponent10_4)(
+    CppTypeFor<TypeCategory::Real, 10> x) {
+  return Exponent<CppTypeFor<TypeCategory::Integer, 4>>(x);
+}
+CppTypeFor<TypeCategory::Integer, 8> RTNAME(Exponent10_8)(
+    CppTypeFor<TypeCategory::Real, 10> x) {
+  return Exponent<CppTypeFor<TypeCategory::Integer, 8>>(x);
+}
+#elif LONG_DOUBLE == 128
+CppTypeFor<TypeCategory::Integer, 4> RTNAME(Exponent16_4)(
+    CppTypeFor<TypeCategory::Real, 16> x) {
+  return Exponent<CppTypeFor<TypeCategory::Integer, 4>>(x);
+}
+CppTypeFor<TypeCategory::Integer, 8> RTNAME(Exponent16_8)(
+    CppTypeFor<TypeCategory::Real, 16> x) {
+  return Exponent<CppTypeFor<TypeCategory::Integer, 8>>(x);
+}
+#endif
+
+CppTypeFor<TypeCategory::Integer, 1> RTNAME(Floor4_1)(
+    CppTypeFor<TypeCategory::Real, 4> x) {
+  return Floor<CppTypeFor<TypeCategory::Integer, 1>>(x);
+}
+CppTypeFor<TypeCategory::Integer, 2> RTNAME(Floor4_2)(
+    CppTypeFor<TypeCategory::Real, 4> x) {
+  return Floor<CppTypeFor<TypeCategory::Integer, 2>>(x);
+}
+CppTypeFor<TypeCategory::Integer, 4> RTNAME(Floor4_4)(
+    CppTypeFor<TypeCategory::Real, 4> x) {
+  return Floor<CppTypeFor<TypeCategory::Integer, 4>>(x);
+}
+CppTypeFor<TypeCategory::Integer, 8> RTNAME(Floor4_8)(
+    CppTypeFor<TypeCategory::Real, 4> x) {
+  return Floor<CppTypeFor<TypeCategory::Integer, 8>>(x);
+}
+CppTypeFor<TypeCategory::Integer, 16> RTNAME(Floor4_16)(
+    CppTypeFor<TypeCategory::Real, 4> x) {
+  return Floor<CppTypeFor<TypeCategory::Integer, 16>>(x);
+}
+CppTypeFor<TypeCategory::Integer, 1> RTNAME(Floor8_1)(
+    CppTypeFor<TypeCategory::Real, 8> x) {
+  return Floor<CppTypeFor<TypeCategory::Integer, 1>>(x);
+}
+CppTypeFor<TypeCategory::Integer, 2> RTNAME(Floor8_2)(
+    CppTypeFor<TypeCategory::Real, 8> x) {
+  return Floor<CppTypeFor<TypeCategory::Integer, 2>>(x);
+}
+CppTypeFor<TypeCategory::Integer, 4> RTNAME(Floor8_4)(
+    CppTypeFor<TypeCategory::Real, 8> x) {
+  return Floor<CppTypeFor<TypeCategory::Integer, 4>>(x);
+}
+CppTypeFor<TypeCategory::Integer, 8> RTNAME(Floor8_8)(
+    CppTypeFor<TypeCategory::Real, 8> x) {
+  return Floor<CppTypeFor<TypeCategory::Integer, 8>>(x);
+}
+CppTypeFor<TypeCategory::Integer, 16> RTNAME(Floor8_16)(
+    CppTypeFor<TypeCategory::Real, 8> x) {
+  return Floor<CppTypeFor<TypeCategory::Integer, 16>>(x);
+}
+#if LONG_DOUBLE == 80
+CppTypeFor<TypeCategory::Integer, 1> RTNAME(Floor10_1)(
+    CppTypeFor<TypeCategory::Real, 10> x) {
+  return Floor<CppTypeFor<TypeCategory::Integer, 1>>(x);
+}
+CppTypeFor<TypeCategory::Integer, 2> RTNAME(Floor10_2)(
+    CppTypeFor<TypeCategory::Real, 10> x) {
+  return Floor<CppTypeFor<TypeCategory::Integer, 2>>(x);
+}
+CppTypeFor<TypeCategory::Integer, 4> RTNAME(Floor10_4)(
+    CppTypeFor<TypeCategory::Real, 10> x) {
+  return Floor<CppTypeFor<TypeCategory::Integer, 4>>(x);
+}
+CppTypeFor<TypeCategory::Integer, 8> RTNAME(Floor10_8)(
+    CppTypeFor<TypeCategory::Real, 10> x) {
+  return Floor<CppTypeFor<TypeCategory::Integer, 8>>(x);
+}
+CppTypeFor<TypeCategory::Integer, 16> RTNAME(Floor10_16)(
+    CppTypeFor<TypeCategory::Real, 10> x) {
+  return Floor<CppTypeFor<TypeCategory::Integer, 16>>(x);
+}
+#else
+CppTypeFor<TypeCategory::Integer, 1> RTNAME(Floor16_1)(
+    CppTypeFor<TypeCategory::Real, 16> x) {
+  return Floor<CppTypeFor<TypeCategory::Integer, 1>>(x);
+}
+CppTypeFor<TypeCategory::Integer, 2> RTNAME(Floor16_2)(
+    CppTypeFor<TypeCategory::Real, 16> x) {
+  return Floor<CppTypeFor<TypeCategory::Integer, 2>>(x);
+}
+CppTypeFor<TypeCategory::Integer, 4> RTNAME(Floor16_4)(
+    CppTypeFor<TypeCategory::Real, 16> x) {
+  return Floor<CppTypeFor<TypeCategory::Integer, 4>>(x);
+}
+CppTypeFor<TypeCategory::Integer, 8> RTNAME(Floor16_8)(
+    CppTypeFor<TypeCategory::Real, 16> x) {
+  return Floor<CppTypeFor<TypeCategory::Integer, 8>>(x);
+}
+CppTypeFor<TypeCategory::Integer, 16> RTNAME(Floor16_16)(
+    CppTypeFor<TypeCategory::Real, 16> x) {
+  return Floor<CppTypeFor<TypeCategory::Integer, 16>>(x);
+}
+#endif
+
+CppTypeFor<TypeCategory::Real, 4> RTNAME(Fraction4)(
+    CppTypeFor<TypeCategory::Real, 4> x) {
+  return Fraction(x);
+}
+CppTypeFor<TypeCategory::Real, 8> RTNAME(Fraction8)(
+    CppTypeFor<TypeCategory::Real, 8> x) {
+  return Fraction(x);
+}
+#if LONG_DOUBLE == 80
+CppTypeFor<TypeCategory::Real, 10> RTNAME(Fraction10)(
+    CppTypeFor<TypeCategory::Real, 10> x) {
+  return Fraction(x);
+}
+#elif LONG_DOUBLE == 128
+CppTypeFor<TypeCategory::Real, 16> RTNAME(Fraction16)(
+    CppTypeFor<TypeCategory::Real, 16> x) {
+  return Fraction(x);
+}
+#endif
+
+CppTypeFor<TypeCategory::Integer, 1> RTNAME(ModInteger1)(
+    CppTypeFor<TypeCategory::Integer, 1> x,
+    CppTypeFor<TypeCategory::Integer, 1> p) {
+  return IntMod<false>(x, p);
+}
+CppTypeFor<TypeCategory::Integer, 2> RTNAME(ModInteger2)(
+    CppTypeFor<TypeCategory::Integer, 2> x,
+    CppTypeFor<TypeCategory::Integer, 2> p) {
+  return IntMod<false>(x, p);
+}
+CppTypeFor<TypeCategory::Integer, 4> RTNAME(ModInteger4)(
+    CppTypeFor<TypeCategory::Integer, 4> x,
+    CppTypeFor<TypeCategory::Integer, 4> p) {
+  return IntMod<false>(x, p);
+}
+CppTypeFor<TypeCategory::Integer, 8> RTNAME(ModInteger8)(
+    CppTypeFor<TypeCategory::Integer, 8> x,
+    CppTypeFor<TypeCategory::Integer, 8> p) {
+  return IntMod<false>(x, p);
+}
+CppTypeFor<TypeCategory::Integer, 16> RTNAME(ModInteger16)(
+    CppTypeFor<TypeCategory::Integer, 16> x,
+    CppTypeFor<TypeCategory::Integer, 16> p) {
+  return IntMod<false>(x, p);
+}
+CppTypeFor<TypeCategory::Real, 4> RTNAME(ModReal4)(
+    CppTypeFor<TypeCategory::Real, 4> x, CppTypeFor<TypeCategory::Real, 4> p) {
+  return RealMod<false>(x, p);
+}
+CppTypeFor<TypeCategory::Real, 8> RTNAME(ModReal8)(
+    CppTypeFor<TypeCategory::Real, 8> x, CppTypeFor<TypeCategory::Real, 8> p) {
+  return RealMod<false>(x, p);
+}
+#if LONG_DOUBLE == 80
+CppTypeFor<TypeCategory::Real, 10> RTNAME(ModReal10)(
+    CppTypeFor<TypeCategory::Real, 10> x,
+    CppTypeFor<TypeCategory::Real, 10> p) {
+  return RealMod<false>(x, p);
+}
+#elif LONG_DOUBLE == 128
+CppTypeFor<TypeCategory::Real, 16> RTNAME(ModReal16)(
+    CppTypeFor<TypeCategory::Real, 16> x,
+    CppTypeFor<TypeCategory::Real, 16> p) {
+  return RealMod<false>(x, p);
+}
+#endif
+
+CppTypeFor<TypeCategory::Integer, 1> RTNAME(ModuloInteger1)(
+    CppTypeFor<TypeCategory::Integer, 1> x,
+    CppTypeFor<TypeCategory::Integer, 1> p) {
+  return IntMod<true>(x, p);
+}
+CppTypeFor<TypeCategory::Integer, 2> RTNAME(ModuloInteger2)(
+    CppTypeFor<TypeCategory::Integer, 2> x,
+    CppTypeFor<TypeCategory::Integer, 2> p) {
+  return IntMod<true>(x, p);
+}
+CppTypeFor<TypeCategory::Integer, 4> RTNAME(ModuloInteger4)(
+    CppTypeFor<TypeCategory::Integer, 4> x,
+    CppTypeFor<TypeCategory::Integer, 4> p) {
+  return IntMod<true>(x, p);
+}
+CppTypeFor<TypeCategory::Integer, 8> RTNAME(ModuloInteger8)(
+    CppTypeFor<TypeCategory::Integer, 8> x,
+    CppTypeFor<TypeCategory::Integer, 8> p) {
+  return IntMod<true>(x, p);
+}
+CppTypeFor<TypeCategory::Integer, 16> RTNAME(ModuloInteger16)(
+    CppTypeFor<TypeCategory::Integer, 16> x,
+    CppTypeFor<TypeCategory::Integer, 16> p) {
+  return IntMod<true>(x, p);
+}
+CppTypeFor<TypeCategory::Real, 4> RTNAME(ModuloReal4)(
+    CppTypeFor<TypeCategory::Real, 4> x, CppTypeFor<TypeCategory::Real, 4> p) {
+  return RealMod<true>(x, p);
+}
+CppTypeFor<TypeCategory::Real, 8> RTNAME(ModuloReal8)(
+    CppTypeFor<TypeCategory::Real, 8> x, CppTypeFor<TypeCategory::Real, 8> p) {
+  return RealMod<true>(x, p);
+}
+#if LONG_DOUBLE == 80
+CppTypeFor<TypeCategory::Real, 10> RTNAME(ModuloReal10)(
+    CppTypeFor<TypeCategory::Real, 10> x,
+    CppTypeFor<TypeCategory::Real, 10> p) {
+  return RealMod<true>(x, p);
+}
+#elif LONG_DOUBLE == 128
+CppTypeFor<TypeCategory::Real, 16> RTNAME(ModuloReal16)(
+    CppTypeFor<TypeCategory::Real, 16> x,
+    CppTypeFor<TypeCategory::Real, 16> p) {
+  return RealMod<true>(x, p);
+}
+#endif
+
+CppTypeFor<TypeCategory::Real, 4> RTNAME(Nearest4)(
+    CppTypeFor<TypeCategory::Real, 4> x, bool positive) {
+  return Nearest<24>(x, positive);
+}
+CppTypeFor<TypeCategory::Real, 8> RTNAME(Nearest8)(
+    CppTypeFor<TypeCategory::Real, 8> x, bool positive) {
+  return Nearest<53>(x, positive);
+}
+#if LONG_DOUBLE == 80
+CppTypeFor<TypeCategory::Real, 10> RTNAME(Nearest10)(
+    CppTypeFor<TypeCategory::Real, 10> x, bool positive) {
+  return Nearest<64>(x, positive);
+}
+#elif LONG_DOUBLE == 128
+CppTypeFor<TypeCategory::Real, 16> RTNAME(Nearest16)(
+    CppTypeFor<TypeCategory::Real, 16> x, bool positive) {
+  return Nearest<113>(x, positive);
+}
+#endif
+
+CppTypeFor<TypeCategory::Integer, 1> RTNAME(Nint4_1)(
+    CppTypeFor<TypeCategory::Real, 4> x) {
+  return Anint<CppTypeFor<TypeCategory::Integer, 1>>(x);
+}
+CppTypeFor<TypeCategory::Integer, 2> RTNAME(Nint4_2)(
+    CppTypeFor<TypeCategory::Real, 4> x) {
+  return Anint<CppTypeFor<TypeCategory::Integer, 2>>(x);
+}
+CppTypeFor<TypeCategory::Integer, 4> RTNAME(Nint4_4)(
+    CppTypeFor<TypeCategory::Real, 4> x) {
+  return Anint<CppTypeFor<TypeCategory::Integer, 4>>(x);
+}
+CppTypeFor<TypeCategory::Integer, 8> RTNAME(Nint4_8)(
+    CppTypeFor<TypeCategory::Real, 4> x) {
+  return Anint<CppTypeFor<TypeCategory::Integer, 8>>(x);
+}
+CppTypeFor<TypeCategory::Integer, 16> RTNAME(Nint4_16)(
+    CppTypeFor<TypeCategory::Real, 4> x) {
+  return Anint<CppTypeFor<TypeCategory::Integer, 16>>(x);
+}
+CppTypeFor<TypeCategory::Integer, 1> RTNAME(Nint8_1)(
+    CppTypeFor<TypeCategory::Real, 8> x) {
+  return Anint<CppTypeFor<TypeCategory::Integer, 1>>(x);
+}
+CppTypeFor<TypeCategory::Integer, 2> RTNAME(Nint8_2)(
+    CppTypeFor<TypeCategory::Real, 8> x) {
+  return Anint<CppTypeFor<TypeCategory::Integer, 2>>(x);
+}
+CppTypeFor<TypeCategory::Integer, 4> RTNAME(Nint8_4)(
+    CppTypeFor<TypeCategory::Real, 8> x) {
+  return Anint<CppTypeFor<TypeCategory::Integer, 4>>(x);
+}
+CppTypeFor<TypeCategory::Integer, 8> RTNAME(Nint8_8)(
+    CppTypeFor<TypeCategory::Real, 8> x) {
+  return Anint<CppTypeFor<TypeCategory::Integer, 8>>(x);
+}
+CppTypeFor<TypeCategory::Integer, 16> RTNAME(Nint8_16)(
+    CppTypeFor<TypeCategory::Real, 8> x) {
+  return Anint<CppTypeFor<TypeCategory::Integer, 16>>(x);
+}
+#if LONG_DOUBLE == 80
+CppTypeFor<TypeCategory::Integer, 1> RTNAME(Nint10_1)(
+    CppTypeFor<TypeCategory::Real, 10> x) {
+  return Anint<CppTypeFor<TypeCategory::Integer, 1>>(x);
+}
+CppTypeFor<TypeCategory::Integer, 2> RTNAME(Nint10_2)(
+    CppTypeFor<TypeCategory::Real, 10> x) {
+  return Anint<CppTypeFor<TypeCategory::Integer, 2>>(x);
+}
+CppTypeFor<TypeCategory::Integer, 4> RTNAME(Nint10_4)(
+    CppTypeFor<TypeCategory::Real, 10> x) {
+  return Anint<CppTypeFor<TypeCategory::Integer, 4>>(x);
+}
+CppTypeFor<TypeCategory::Integer, 8> RTNAME(Nint10_8)(
+    CppTypeFor<TypeCategory::Real, 10> x) {
+  return Anint<CppTypeFor<TypeCategory::Integer, 8>>(x);
+}
+CppTypeFor<TypeCategory::Integer, 16> RTNAME(Nint10_16)(
+    CppTypeFor<TypeCategory::Real, 10> x) {
+  return Anint<CppTypeFor<TypeCategory::Integer, 16>>(x);
+}
+#else
+CppTypeFor<TypeCategory::Integer, 1> RTNAME(Nint16_1)(
+    CppTypeFor<TypeCategory::Real, 16> x) {
+  return Anint<CppTypeFor<TypeCategory::Integer, 1>>(x);
+}
+CppTypeFor<TypeCategory::Integer, 2> RTNAME(Nint16_2)(
+    CppTypeFor<TypeCategory::Real, 16> x) {
+  return Anint<CppTypeFor<TypeCategory::Integer, 2>>(x);
+}
+CppTypeFor<TypeCategory::Integer, 4> RTNAME(Nint16_4)(
+    CppTypeFor<TypeCategory::Real, 16> x) {
+  return Anint<CppTypeFor<TypeCategory::Integer, 4>>(x);
+}
+CppTypeFor<TypeCategory::Integer, 8> RTNAME(Nint16_8)(
+    CppTypeFor<TypeCategory::Real, 16> x) {
+  return Anint<CppTypeFor<TypeCategory::Integer, 8>>(x);
+}
+CppTypeFor<TypeCategory::Integer, 16> RTNAME(Nint16_16)(
+    CppTypeFor<TypeCategory::Real, 16> x) {
+  return Anint<CppTypeFor<TypeCategory::Integer, 16>>(x);
+}
+#endif
+
+CppTypeFor<TypeCategory::Real, 4> RTNAME(RRSpacing4)(
+    CppTypeFor<TypeCategory::Real, 4> x) {
+  return RRSpacing<24>(x);
+}
+CppTypeFor<TypeCategory::Real, 8> RTNAME(RRSpacing8)(
+    CppTypeFor<TypeCategory::Real, 8> x) {
+  return RRSpacing<53>(x);
+}
+#if LONG_DOUBLE == 80
+CppTypeFor<TypeCategory::Real, 10> RTNAME(RRSpacing10)(
+    CppTypeFor<TypeCategory::Real, 10> x) {
+  return RRSpacing<64>(x);
+}
+#elif LONG_DOUBLE == 128
+CppTypeFor<TypeCategory::Real, 16> RTNAME(RRSpacing16)(
+    CppTypeFor<TypeCategory::Real, 16> x) {
+  return RRSpacing<113>(x);
+}
+#endif
+
+CppTypeFor<TypeCategory::Real, 4> RTNAME(SetExponent4)(
+    CppTypeFor<TypeCategory::Real, 4> x, std::int64_t p) {
+  return SetExponent(x, p);
+}
+CppTypeFor<TypeCategory::Real, 8> RTNAME(SetExponent8)(
+    CppTypeFor<TypeCategory::Real, 8> x, std::int64_t p) {
+  return SetExponent(x, p);
+}
+#if LONG_DOUBLE == 80
+CppTypeFor<TypeCategory::Real, 10> RTNAME(SetExponent10)(
+    CppTypeFor<TypeCategory::Real, 10> x, std::int64_t p) {
+  return SetExponent(x, p);
+}
+#elif LONG_DOUBLE == 128
+CppTypeFor<TypeCategory::Real, 16> RTNAME(SetExponent16)(
+    CppTypeFor<TypeCategory::Real, 16> x, std::int64_t p) {
+  return SetExponent(x, p);
+}
+#endif
+
+CppTypeFor<TypeCategory::Real, 4> RTNAME(Scale4)(
+    CppTypeFor<TypeCategory::Real, 4> x, std::int64_t p) {
+  return Scale(x, p);
+}
+CppTypeFor<TypeCategory::Real, 8> RTNAME(Scale8)(
+    CppTypeFor<TypeCategory::Real, 8> x, std::int64_t p) {
+  return Scale(x, p);
+}
+#if LONG_DOUBLE == 80
+CppTypeFor<TypeCategory::Real, 10> RTNAME(Scale10)(
+    CppTypeFor<TypeCategory::Real, 10> x, std::int64_t p) {
+  return Scale(x, p);
+}
+#elif LONG_DOUBLE == 128
+CppTypeFor<TypeCategory::Real, 16> RTNAME(Scale16)(
+    CppTypeFor<TypeCategory::Real, 16> x, std::int64_t p) {
+  return Scale(x, p);
+}
+#endif
+
+CppTypeFor<TypeCategory::Real, 4> RTNAME(Spacing4)(
+    CppTypeFor<TypeCategory::Real, 4> x) {
+  return Spacing<24>(x);
+}
+CppTypeFor<TypeCategory::Real, 8> RTNAME(Spacing8)(
+    CppTypeFor<TypeCategory::Real, 8> x) {
+  return Spacing<53>(x);
+}
+#if LONG_DOUBLE == 80
+CppTypeFor<TypeCategory::Real, 10> RTNAME(Spacing10)(
+    CppTypeFor<TypeCategory::Real, 10> x) {
+  return Spacing<64>(x);
+}
+#elif LONG_DOUBLE == 128
+CppTypeFor<TypeCategory::Real, 16> RTNAME(Spacing16)(
+    CppTypeFor<TypeCategory::Real, 16> x) {
+  return Spacing<113>(x);
+}
+#endif
+} // extern "C"
+} // namespace Fortran::runtime
diff --git a/runtime/numeric.h b/runtime/numeric.h
new file mode 100644
index 0000000..d9c9e9e
--- /dev/null
+++ b/runtime/numeric.h
@@ -0,0 +1,329 @@
+//===-- runtime/numeric.h ---------------------------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+// Defines API between compiled code and the implementations of various numeric
+// intrinsic functions in the runtime library.
+
+#ifndef FORTRAN_RUNTIME_NUMERIC_H_
+#define FORTRAN_RUNTIME_NUMERIC_H_
+
+#include "cpp-type.h"
+#include "entry-names.h"
+
+namespace Fortran::runtime {
+extern "C" {
+
+// AINT
+CppTypeFor<TypeCategory::Real, 4> RTNAME(Aint4_4)(
+    CppTypeFor<TypeCategory::Real, 4>);
+CppTypeFor<TypeCategory::Real, 8> RTNAME(Aint4_8)(
+    CppTypeFor<TypeCategory::Real, 4>);
+CppTypeFor<TypeCategory::Real, 10> RTNAME(Aint4_10)(
+    CppTypeFor<TypeCategory::Real, 4>);
+CppTypeFor<TypeCategory::Real, 16> RTNAME(Aint4_16)(
+    CppTypeFor<TypeCategory::Real, 4>);
+CppTypeFor<TypeCategory::Real, 4> RTNAME(Aint8_4)(
+    CppTypeFor<TypeCategory::Real, 8>);
+CppTypeFor<TypeCategory::Real, 8> RTNAME(Aint8_8)(
+    CppTypeFor<TypeCategory::Real, 8>);
+CppTypeFor<TypeCategory::Real, 10> RTNAME(Aint8_10)(
+    CppTypeFor<TypeCategory::Real, 8>);
+CppTypeFor<TypeCategory::Real, 16> RTNAME(Aint8_16)(
+    CppTypeFor<TypeCategory::Real, 8>);
+CppTypeFor<TypeCategory::Real, 4> RTNAME(Aint10_4)(
+    CppTypeFor<TypeCategory::Real, 10>);
+CppTypeFor<TypeCategory::Real, 8> RTNAME(Aint10_8)(
+    CppTypeFor<TypeCategory::Real, 10>);
+CppTypeFor<TypeCategory::Real, 10> RTNAME(Aint10_10)(
+    CppTypeFor<TypeCategory::Real, 10>);
+CppTypeFor<TypeCategory::Real, 4> RTNAME(Aint16_4)(
+    CppTypeFor<TypeCategory::Real, 16>);
+CppTypeFor<TypeCategory::Real, 8> RTNAME(Aint16_8)(
+    CppTypeFor<TypeCategory::Real, 16>);
+CppTypeFor<TypeCategory::Real, 16> RTNAME(Aint16_16)(
+    CppTypeFor<TypeCategory::Real, 16>);
+
+// ANINT
+CppTypeFor<TypeCategory::Real, 4> RTNAME(Anint4_4)(
+    CppTypeFor<TypeCategory::Real, 4>);
+CppTypeFor<TypeCategory::Real, 8> RTNAME(Anint4_8)(
+    CppTypeFor<TypeCategory::Real, 4>);
+CppTypeFor<TypeCategory::Real, 10> RTNAME(Anint4_10)(
+    CppTypeFor<TypeCategory::Real, 4>);
+CppTypeFor<TypeCategory::Real, 16> RTNAME(Anint4_16)(
+    CppTypeFor<TypeCategory::Real, 4>);
+CppTypeFor<TypeCategory::Real, 4> RTNAME(Anint8_4)(
+    CppTypeFor<TypeCategory::Real, 8>);
+CppTypeFor<TypeCategory::Real, 8> RTNAME(Anint8_8)(
+    CppTypeFor<TypeCategory::Real, 8>);
+CppTypeFor<TypeCategory::Real, 10> RTNAME(Anint8_10)(
+    CppTypeFor<TypeCategory::Real, 8>);
+CppTypeFor<TypeCategory::Real, 16> RTNAME(Anint8_16)(
+    CppTypeFor<TypeCategory::Real, 8>);
+CppTypeFor<TypeCategory::Real, 4> RTNAME(Anint10_4)(
+    CppTypeFor<TypeCategory::Real, 10>);
+CppTypeFor<TypeCategory::Real, 8> RTNAME(Anint10_8)(
+    CppTypeFor<TypeCategory::Real, 10>);
+CppTypeFor<TypeCategory::Real, 10> RTNAME(Anint10_10)(
+    CppTypeFor<TypeCategory::Real, 10>);
+CppTypeFor<TypeCategory::Real, 4> RTNAME(Anint16_4)(
+    CppTypeFor<TypeCategory::Real, 16>);
+CppTypeFor<TypeCategory::Real, 8> RTNAME(Anint16_8)(
+    CppTypeFor<TypeCategory::Real, 16>);
+CppTypeFor<TypeCategory::Real, 16> RTNAME(Anint16_16)(
+    CppTypeFor<TypeCategory::Real, 16>);
+
+// CEILING
+CppTypeFor<TypeCategory::Integer, 1> RTNAME(Ceiling4_1)(
+    CppTypeFor<TypeCategory::Real, 4>);
+CppTypeFor<TypeCategory::Integer, 2> RTNAME(Ceiling4_2)(
+    CppTypeFor<TypeCategory::Real, 4>);
+CppTypeFor<TypeCategory::Integer, 4> RTNAME(Ceiling4_4)(
+    CppTypeFor<TypeCategory::Real, 4>);
+CppTypeFor<TypeCategory::Integer, 8> RTNAME(Ceiling4_8)(
+    CppTypeFor<TypeCategory::Real, 4>);
+CppTypeFor<TypeCategory::Integer, 16> RTNAME(Ceiling4_16)(
+    CppTypeFor<TypeCategory::Real, 4>);
+CppTypeFor<TypeCategory::Integer, 1> RTNAME(Ceiling8_1)(
+    CppTypeFor<TypeCategory::Real, 8>);
+CppTypeFor<TypeCategory::Integer, 2> RTNAME(Ceiling8_2)(
+    CppTypeFor<TypeCategory::Real, 8>);
+CppTypeFor<TypeCategory::Integer, 4> RTNAME(Ceiling8_4)(
+    CppTypeFor<TypeCategory::Real, 8>);
+CppTypeFor<TypeCategory::Integer, 8> RTNAME(Ceiling8_8)(
+    CppTypeFor<TypeCategory::Real, 8>);
+CppTypeFor<TypeCategory::Integer, 16> RTNAME(Ceiling8_16)(
+    CppTypeFor<TypeCategory::Real, 8>);
+CppTypeFor<TypeCategory::Integer, 1> RTNAME(Ceiling10_1)(
+    CppTypeFor<TypeCategory::Real, 10>);
+CppTypeFor<TypeCategory::Integer, 2> RTNAME(Ceiling10_2)(
+    CppTypeFor<TypeCategory::Real, 10>);
+CppTypeFor<TypeCategory::Integer, 4> RTNAME(Ceiling10_4)(
+    CppTypeFor<TypeCategory::Real, 10>);
+CppTypeFor<TypeCategory::Integer, 8> RTNAME(Ceiling10_8)(
+    CppTypeFor<TypeCategory::Real, 10>);
+CppTypeFor<TypeCategory::Integer, 16> RTNAME(Ceiling10_16)(
+    CppTypeFor<TypeCategory::Real, 10>);
+CppTypeFor<TypeCategory::Integer, 1> RTNAME(Ceiling16_1)(
+    CppTypeFor<TypeCategory::Real, 16>);
+CppTypeFor<TypeCategory::Integer, 2> RTNAME(Ceiling16_2)(
+    CppTypeFor<TypeCategory::Real, 16>);
+CppTypeFor<TypeCategory::Integer, 4> RTNAME(Ceiling16_4)(
+    CppTypeFor<TypeCategory::Real, 16>);
+CppTypeFor<TypeCategory::Integer, 8> RTNAME(Ceiling16_8)(
+    CppTypeFor<TypeCategory::Real, 16>);
+CppTypeFor<TypeCategory::Integer, 16> RTNAME(Ceiling16_16)(
+    CppTypeFor<TypeCategory::Real, 16>);
+
+// EXPONENT is defined to return default INTEGER; support INTEGER(4 & 8)
+CppTypeFor<TypeCategory::Integer, 4> RTNAME(Exponent4_4)(
+    CppTypeFor<TypeCategory::Real, 4>);
+CppTypeFor<TypeCategory::Integer, 8> RTNAME(Exponent4_8)(
+    CppTypeFor<TypeCategory::Real, 4>);
+CppTypeFor<TypeCategory::Integer, 4> RTNAME(Exponent8_4)(
+    CppTypeFor<TypeCategory::Real, 8>);
+CppTypeFor<TypeCategory::Integer, 8> RTNAME(Exponent8_8)(
+    CppTypeFor<TypeCategory::Real, 8>);
+CppTypeFor<TypeCategory::Integer, 4> RTNAME(Exponent10_4)(
+    CppTypeFor<TypeCategory::Real, 10>);
+CppTypeFor<TypeCategory::Integer, 8> RTNAME(Exponent10_8)(
+    CppTypeFor<TypeCategory::Real, 10>);
+CppTypeFor<TypeCategory::Integer, 4> RTNAME(Exponent16_4)(
+    CppTypeFor<TypeCategory::Real, 16>);
+CppTypeFor<TypeCategory::Integer, 8> RTNAME(Exponent16_8)(
+    CppTypeFor<TypeCategory::Real, 16>);
+
+// FLOOR
+CppTypeFor<TypeCategory::Integer, 1> RTNAME(Floor4_1)(
+    CppTypeFor<TypeCategory::Real, 4>);
+CppTypeFor<TypeCategory::Integer, 2> RTNAME(Floor4_2)(
+    CppTypeFor<TypeCategory::Real, 4>);
+CppTypeFor<TypeCategory::Integer, 4> RTNAME(Floor4_4)(
+    CppTypeFor<TypeCategory::Real, 4>);
+CppTypeFor<TypeCategory::Integer, 8> RTNAME(Floor4_8)(
+    CppTypeFor<TypeCategory::Real, 4>);
+CppTypeFor<TypeCategory::Integer, 16> RTNAME(Floor4_16)(
+    CppTypeFor<TypeCategory::Real, 4>);
+CppTypeFor<TypeCategory::Integer, 1> RTNAME(Floor8_1)(
+    CppTypeFor<TypeCategory::Real, 8>);
+CppTypeFor<TypeCategory::Integer, 2> RTNAME(Floor8_2)(
+    CppTypeFor<TypeCategory::Real, 8>);
+CppTypeFor<TypeCategory::Integer, 4> RTNAME(Floor8_4)(
+    CppTypeFor<TypeCategory::Real, 8>);
+CppTypeFor<TypeCategory::Integer, 8> RTNAME(Floor8_8)(
+    CppTypeFor<TypeCategory::Real, 8>);
+CppTypeFor<TypeCategory::Integer, 16> RTNAME(Floor8_16)(
+    CppTypeFor<TypeCategory::Real, 8>);
+CppTypeFor<TypeCategory::Integer, 1> RTNAME(Floor10_1)(
+    CppTypeFor<TypeCategory::Real, 10>);
+CppTypeFor<TypeCategory::Integer, 2> RTNAME(Floor10_2)(
+    CppTypeFor<TypeCategory::Real, 10>);
+CppTypeFor<TypeCategory::Integer, 4> RTNAME(Floor10_4)(
+    CppTypeFor<TypeCategory::Real, 10>);
+CppTypeFor<TypeCategory::Integer, 8> RTNAME(Floor10_8)(
+    CppTypeFor<TypeCategory::Real, 10>);
+CppTypeFor<TypeCategory::Integer, 16> RTNAME(Floor10_16)(
+    CppTypeFor<TypeCategory::Real, 10>);
+CppTypeFor<TypeCategory::Integer, 1> RTNAME(Floor16_1)(
+    CppTypeFor<TypeCategory::Real, 16>);
+CppTypeFor<TypeCategory::Integer, 2> RTNAME(Floor16_2)(
+    CppTypeFor<TypeCategory::Real, 16>);
+CppTypeFor<TypeCategory::Integer, 4> RTNAME(Floor16_4)(
+    CppTypeFor<TypeCategory::Real, 16>);
+CppTypeFor<TypeCategory::Integer, 8> RTNAME(Floor16_8)(
+    CppTypeFor<TypeCategory::Real, 16>);
+CppTypeFor<TypeCategory::Integer, 16> RTNAME(Floor16_16)(
+    CppTypeFor<TypeCategory::Real, 16>);
+
+// FRACTION
+CppTypeFor<TypeCategory::Real, 4> RTNAME(Fraction4)(
+    CppTypeFor<TypeCategory::Real, 4>);
+CppTypeFor<TypeCategory::Real, 8> RTNAME(Fraction8)(
+    CppTypeFor<TypeCategory::Real, 8>);
+CppTypeFor<TypeCategory::Real, 10> RTNAME(Fraction10)(
+    CppTypeFor<TypeCategory::Real, 10>);
+CppTypeFor<TypeCategory::Real, 16> RTNAME(Fraction16)(
+    CppTypeFor<TypeCategory::Real, 16>);
+
+// MOD & MODULO
+CppTypeFor<TypeCategory::Integer, 1> RTNAME(ModInteger1)(
+    CppTypeFor<TypeCategory::Integer, 1>, CppTypeFor<TypeCategory::Integer, 1>);
+CppTypeFor<TypeCategory::Integer, 2> RTNAME(ModInteger2)(
+    CppTypeFor<TypeCategory::Integer, 2>, CppTypeFor<TypeCategory::Integer, 2>);
+CppTypeFor<TypeCategory::Integer, 4> RTNAME(ModInteger4)(
+    CppTypeFor<TypeCategory::Integer, 4>, CppTypeFor<TypeCategory::Integer, 4>);
+CppTypeFor<TypeCategory::Integer, 8> RTNAME(ModInteger8)(
+    CppTypeFor<TypeCategory::Integer, 8>, CppTypeFor<TypeCategory::Integer, 8>);
+CppTypeFor<TypeCategory::Integer, 16> RTNAME(ModInteger16)(
+    CppTypeFor<TypeCategory::Integer, 16>,
+    CppTypeFor<TypeCategory::Integer, 16>);
+CppTypeFor<TypeCategory::Real, 4> RTNAME(ModReal4)(
+    CppTypeFor<TypeCategory::Real, 4>, CppTypeFor<TypeCategory::Real, 4>);
+CppTypeFor<TypeCategory::Real, 8> RTNAME(ModReal8)(
+    CppTypeFor<TypeCategory::Real, 8>, CppTypeFor<TypeCategory::Real, 8>);
+CppTypeFor<TypeCategory::Real, 10> RTNAME(ModReal10)(
+    CppTypeFor<TypeCategory::Real, 10>, CppTypeFor<TypeCategory::Real, 10>);
+CppTypeFor<TypeCategory::Real, 16> RTNAME(ModReal16)(
+    CppTypeFor<TypeCategory::Real, 16>, CppTypeFor<TypeCategory::Real, 16>);
+
+CppTypeFor<TypeCategory::Integer, 1> RTNAME(ModuloInteger1)(
+    CppTypeFor<TypeCategory::Integer, 1>, CppTypeFor<TypeCategory::Integer, 1>);
+CppTypeFor<TypeCategory::Integer, 2> RTNAME(ModuloInteger2)(
+    CppTypeFor<TypeCategory::Integer, 2>, CppTypeFor<TypeCategory::Integer, 2>);
+CppTypeFor<TypeCategory::Integer, 4> RTNAME(ModuloInteger4)(
+    CppTypeFor<TypeCategory::Integer, 4>, CppTypeFor<TypeCategory::Integer, 4>);
+CppTypeFor<TypeCategory::Integer, 8> RTNAME(ModuloInteger8)(
+    CppTypeFor<TypeCategory::Integer, 8>, CppTypeFor<TypeCategory::Integer, 8>);
+CppTypeFor<TypeCategory::Integer, 16> RTNAME(ModuloInteger16)(
+    CppTypeFor<TypeCategory::Integer, 16>,
+    CppTypeFor<TypeCategory::Integer, 16>);
+CppTypeFor<TypeCategory::Real, 4> RTNAME(ModuloReal4)(
+    CppTypeFor<TypeCategory::Real, 4>, CppTypeFor<TypeCategory::Real, 4>);
+CppTypeFor<TypeCategory::Real, 8> RTNAME(ModuloReal8)(
+    CppTypeFor<TypeCategory::Real, 8>, CppTypeFor<TypeCategory::Real, 8>);
+CppTypeFor<TypeCategory::Real, 10> RTNAME(ModuloReal10)(
+    CppTypeFor<TypeCategory::Real, 10>, CppTypeFor<TypeCategory::Real, 10>);
+CppTypeFor<TypeCategory::Real, 16> RTNAME(ModuloReal16)(
+    CppTypeFor<TypeCategory::Real, 16>, CppTypeFor<TypeCategory::Real, 16>);
+
+// NINT
+CppTypeFor<TypeCategory::Integer, 1> RTNAME(Nint4_1)(
+    CppTypeFor<TypeCategory::Real, 4>);
+CppTypeFor<TypeCategory::Integer, 2> RTNAME(Nint4_2)(
+    CppTypeFor<TypeCategory::Real, 4>);
+CppTypeFor<TypeCategory::Integer, 4> RTNAME(Nint4_4)(
+    CppTypeFor<TypeCategory::Real, 4>);
+CppTypeFor<TypeCategory::Integer, 8> RTNAME(Nint4_8)(
+    CppTypeFor<TypeCategory::Real, 4>);
+CppTypeFor<TypeCategory::Integer, 16> RTNAME(Nint4_16)(
+    CppTypeFor<TypeCategory::Real, 4>);
+CppTypeFor<TypeCategory::Integer, 1> RTNAME(Nint8_1)(
+    CppTypeFor<TypeCategory::Real, 8>);
+CppTypeFor<TypeCategory::Integer, 2> RTNAME(Nint8_2)(
+    CppTypeFor<TypeCategory::Real, 8>);
+CppTypeFor<TypeCategory::Integer, 4> RTNAME(Nint8_4)(
+    CppTypeFor<TypeCategory::Real, 8>);
+CppTypeFor<TypeCategory::Integer, 8> RTNAME(Nint8_8)(
+    CppTypeFor<TypeCategory::Real, 8>);
+CppTypeFor<TypeCategory::Integer, 16> RTNAME(Nint8_16)(
+    CppTypeFor<TypeCategory::Real, 8>);
+CppTypeFor<TypeCategory::Integer, 1> RTNAME(Nint10_1)(
+    CppTypeFor<TypeCategory::Real, 10>);
+CppTypeFor<TypeCategory::Integer, 2> RTNAME(Nint10_2)(
+    CppTypeFor<TypeCategory::Real, 10>);
+CppTypeFor<TypeCategory::Integer, 4> RTNAME(Nint10_4)(
+    CppTypeFor<TypeCategory::Real, 10>);
+CppTypeFor<TypeCategory::Integer, 8> RTNAME(Nint10_8)(
+    CppTypeFor<TypeCategory::Real, 10>);
+CppTypeFor<TypeCategory::Integer, 16> RTNAME(Nint10_16)(
+    CppTypeFor<TypeCategory::Real, 10>);
+CppTypeFor<TypeCategory::Integer, 1> RTNAME(Nint16_1)(
+    CppTypeFor<TypeCategory::Real, 16>);
+CppTypeFor<TypeCategory::Integer, 2> RTNAME(Nint16_2)(
+    CppTypeFor<TypeCategory::Real, 16>);
+CppTypeFor<TypeCategory::Integer, 4> RTNAME(Nint16_4)(
+    CppTypeFor<TypeCategory::Real, 16>);
+CppTypeFor<TypeCategory::Integer, 8> RTNAME(Nint16_8)(
+    CppTypeFor<TypeCategory::Real, 16>);
+CppTypeFor<TypeCategory::Integer, 16> RTNAME(Nint16_16)(
+    CppTypeFor<TypeCategory::Real, 16>);
+
+// NEAREST
+// The second argument to NEAREST is the result of a comparison
+// to zero (i.e., S > 0)
+CppTypeFor<TypeCategory::Real, 4> RTNAME(Nearest4)(
+    CppTypeFor<TypeCategory::Real, 4>, bool positive);
+CppTypeFor<TypeCategory::Real, 8> RTNAME(Nearest8)(
+    CppTypeFor<TypeCategory::Real, 8>, bool positive);
+CppTypeFor<TypeCategory::Real, 10> RTNAME(Nearest10)(
+    CppTypeFor<TypeCategory::Real, 10>, bool positive);
+CppTypeFor<TypeCategory::Real, 16> RTNAME(Nearest16)(
+    CppTypeFor<TypeCategory::Real, 16>, bool positive);
+
+// RRSPACING
+CppTypeFor<TypeCategory::Real, 4> RTNAME(RRSpacing4)(
+    CppTypeFor<TypeCategory::Real, 4>);
+CppTypeFor<TypeCategory::Real, 8> RTNAME(RRSpacing8)(
+    CppTypeFor<TypeCategory::Real, 8>);
+CppTypeFor<TypeCategory::Real, 10> RTNAME(RRSpacing10)(
+    CppTypeFor<TypeCategory::Real, 10>);
+CppTypeFor<TypeCategory::Real, 16> RTNAME(RRSpacing16)(
+    CppTypeFor<TypeCategory::Real, 16>);
+
+// SET_EXPONENT's I= argument can be any INTEGER kind; upcast it to 64-bit
+CppTypeFor<TypeCategory::Real, 4> RTNAME(SetExponent4)(
+    CppTypeFor<TypeCategory::Real, 4>, std::int64_t);
+CppTypeFor<TypeCategory::Real, 8> RTNAME(SetExponent8)(
+    CppTypeFor<TypeCategory::Real, 8>, std::int64_t);
+CppTypeFor<TypeCategory::Real, 10> RTNAME(SetExponent10)(
+    CppTypeFor<TypeCategory::Real, 10>, std::int64_t);
+CppTypeFor<TypeCategory::Real, 16> RTNAME(SetExponent16)(
+    CppTypeFor<TypeCategory::Real, 16>, std::int64_t);
+
+// SCALE
+CppTypeFor<TypeCategory::Real, 4> RTNAME(Scale4)(
+    CppTypeFor<TypeCategory::Real, 4>, std::int64_t);
+CppTypeFor<TypeCategory::Real, 8> RTNAME(Scale8)(
+    CppTypeFor<TypeCategory::Real, 8>, std::int64_t);
+CppTypeFor<TypeCategory::Real, 10> RTNAME(Scale10)(
+    CppTypeFor<TypeCategory::Real, 10>, std::int64_t);
+CppTypeFor<TypeCategory::Real, 16> RTNAME(Scale16)(
+    CppTypeFor<TypeCategory::Real, 16>, std::int64_t);
+
+// SPACING
+CppTypeFor<TypeCategory::Real, 4> RTNAME(Spacing4)(
+    CppTypeFor<TypeCategory::Real, 4>);
+CppTypeFor<TypeCategory::Real, 8> RTNAME(Spacing8)(
+    CppTypeFor<TypeCategory::Real, 8>);
+CppTypeFor<TypeCategory::Real, 10> RTNAME(Spacing10)(
+    CppTypeFor<TypeCategory::Real, 10>);
+CppTypeFor<TypeCategory::Real, 16> RTNAME(Spacing16)(
+    CppTypeFor<TypeCategory::Real, 16>);
+} // extern "C"
+} // namespace Fortran::runtime
+#endif // FORTRAN_RUNTIME_NUMERIC_H_
diff --git a/unittests/RuntimeGTest/CMakeLists.txt b/unittests/RuntimeGTest/CMakeLists.txt
index ad6377e..1d4e2d0 100644
--- a/unittests/RuntimeGTest/CMakeLists.txt
+++ b/unittests/RuntimeGTest/CMakeLists.txt
@@ -1,6 +1,7 @@
 add_flang_unittest(FlangRuntimeTests
   CharacterTest.cpp
   CrashHandlerFixture.cpp
+  Numeric.cpp
   NumericalFormatTest.cpp
   Reduction.cpp
   RuntimeCrashTest.cpp
diff --git a/unittests/RuntimeGTest/Numeric.cpp b/unittests/RuntimeGTest/Numeric.cpp
new file mode 100644
index 0000000..60a878d
--- /dev/null
+++ b/unittests/RuntimeGTest/Numeric.cpp
@@ -0,0 +1,168 @@
+//===-- flang/unittests/RuntimeGTest/Numeric.cpp ----------------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "../../runtime/numeric.h"
+#include "gtest/gtest.h"
+#include <cmath>
+#include <limits>
+
+using namespace Fortran::runtime;
+using Fortran::common::TypeCategory;
+template <int KIND> using Int = CppTypeFor<TypeCategory::Integer, KIND>;
+template <int KIND> using Real = CppTypeFor<TypeCategory::Real, KIND>;
+
+// Simple tests of numeric intrinsic functions using examples from Fortran 2018
+
+TEST(Numeric, Aint) {
+  EXPECT_EQ(RTNAME(Aint4_4)(Real<4>{3.7}), 3.0);
+  EXPECT_EQ(RTNAME(Aint8_4)(Real<8>{-3.7}), -3.0);
+  EXPECT_EQ(RTNAME(Aint8_8)(Real<8>{0}), 0.0);
+  EXPECT_EQ(RTNAME(Aint4_4)(std::numeric_limits<Real<4>>::infinity()),
+      std::numeric_limits<Real<4>>::infinity());
+  EXPECT_TRUE(
+      std::isnan(RTNAME(Aint8_8)(std::numeric_limits<Real<8>>::quiet_NaN())));
+}
+
+TEST(Numeric, Anint) {
+  EXPECT_EQ(RTNAME(Anint4_4)(Real<4>{2.783}), 3.0);
+  EXPECT_EQ(RTNAME(Anint8_4)(Real<8>{-2.783}), -3.0);
+  EXPECT_EQ(RTNAME(Anint4_4)(Real<4>{2.5}), 3.0);
+  EXPECT_EQ(RTNAME(Anint8_4)(Real<8>{-2.5}), -3.0);
+  EXPECT_EQ(RTNAME(Anint8_8)(Real<8>{0}), 0.0);
+  EXPECT_EQ(RTNAME(Anint4_4)(std::numeric_limits<Real<4>>::infinity()),
+      std::numeric_limits<Real<4>>::infinity());
+  EXPECT_TRUE(
+      std::isnan(RTNAME(Aint8_8)(std::numeric_limits<Real<8>>::quiet_NaN())));
+}
+
+TEST(Numeric, Ceiling) {
+  EXPECT_EQ(RTNAME(Ceiling4_4)(Real<4>{3.7}), 4);
+  EXPECT_EQ(RTNAME(Ceiling8_8)(Real<8>{-3.7}), -3);
+  EXPECT_EQ(RTNAME(Ceiling4_1)(Real<4>{0}), 0);
+  EXPECT_EQ(RTNAME(Ceiling4_4)(std::numeric_limits<Real<4>>::infinity()),
+      std::numeric_limits<Int<4>>::min());
+  EXPECT_EQ(RTNAME(Ceiling4_4)(std::numeric_limits<Real<4>>::quiet_NaN()),
+      std::numeric_limits<Int<4>>::min());
+}
+
+TEST(Numeric, Floor) {
+  EXPECT_EQ(RTNAME(Floor4_4)(Real<4>{3.7}), 3);
+  EXPECT_EQ(RTNAME(Floor8_8)(Real<8>{-3.7}), -4);
+  EXPECT_EQ(RTNAME(Floor4_1)(Real<4>{0}), 0);
+  EXPECT_EQ(RTNAME(Floor4_4)(std::numeric_limits<Real<4>>::infinity()),
+      std::numeric_limits<Int<4>>::min());
+  EXPECT_EQ(RTNAME(Floor4_4)(std::numeric_limits<Real<4>>::quiet_NaN()),
+      std::numeric_limits<Int<4>>::min());
+}
+
+TEST(Numeric, Exponent) {
+  EXPECT_EQ(RTNAME(Exponent4_4)(Real<4>{0}), 0);
+  EXPECT_EQ(RTNAME(Exponent4_8)(Real<4>{1.0}), 1);
+  EXPECT_EQ(RTNAME(Exponent8_4)(Real<8>{4.1}), 3);
+  EXPECT_EQ(RTNAME(Exponent8_8)(std::numeric_limits<Real<8>>::infinity()),
+      std::numeric_limits<Int<8>>::max());
+  EXPECT_EQ(RTNAME(Exponent8_8)(std::numeric_limits<Real<8>>::quiet_NaN()),
+      std::numeric_limits<Int<8>>::max());
+}
+
+TEST(Numeric, Fraction) {
+  EXPECT_EQ(RTNAME(Fraction4)(Real<4>{0}), 0);
+  EXPECT_EQ(RTNAME(Fraction8)(Real<8>{3.0}), 0.75);
+  EXPECT_TRUE(
+      std::isnan(RTNAME(Fraction4)(std::numeric_limits<Real<4>>::infinity())));
+  EXPECT_TRUE(
+      std::isnan(RTNAME(Fraction8)(std::numeric_limits<Real<8>>::quiet_NaN())));
+}
+
+TEST(Numeric, Mod) {
+  EXPECT_EQ(RTNAME(ModInteger1)(Int<1>{8}, Int<1>(5)), 3);
+  EXPECT_EQ(RTNAME(ModInteger4)(Int<4>{-8}, Int<4>(5)), -3);
+  EXPECT_EQ(RTNAME(ModInteger2)(Int<2>{8}, Int<2>(-5)), 3);
+  EXPECT_EQ(RTNAME(ModInteger8)(Int<8>{-8}, Int<8>(-5)), -3);
+  EXPECT_EQ(RTNAME(ModReal4)(Real<4>{8.0}, Real<4>(5.0)), 3.0);
+  EXPECT_EQ(RTNAME(ModReal4)(Real<4>{-8.0}, Real<4>(5.0)), -3.0);
+  EXPECT_EQ(RTNAME(ModReal8)(Real<8>{8.0}, Real<8>(-5.0)), 3.0);
+  EXPECT_EQ(RTNAME(ModReal8)(Real<8>{-8.0}, Real<8>(-5.0)), -3.0);
+}
+
+TEST(Numeric, Modulo) {
+  EXPECT_EQ(RTNAME(ModuloInteger1)(Int<1>{8}, Int<1>(5)), 3);
+  EXPECT_EQ(RTNAME(ModuloInteger4)(Int<4>{-8}, Int<4>(5)), 2);
+  EXPECT_EQ(RTNAME(ModuloInteger2)(Int<2>{8}, Int<2>(-5)), -2);
+  EXPECT_EQ(RTNAME(ModuloInteger8)(Int<8>{-8}, Int<8>(-5)), -3);
+  EXPECT_EQ(RTNAME(ModuloReal4)(Real<4>{8.0}, Real<4>(5.0)), 3.0);
+  EXPECT_EQ(RTNAME(ModuloReal4)(Real<4>{-8.0}, Real<4>(5.0)), 2.0);
+  EXPECT_EQ(RTNAME(ModuloReal8)(Real<8>{8.0}, Real<8>(-5.0)), -2.0);
+  EXPECT_EQ(RTNAME(ModuloReal8)(Real<8>{-8.0}, Real<8>(-5.0)), -3.0);
+}
+
+TEST(Numeric, Nearest) {
+  EXPECT_EQ(RTNAME(Nearest4)(Real<4>{0}, true),
+      std::numeric_limits<Real<4>>::denorm_min());
+  EXPECT_EQ(RTNAME(Nearest4)(Real<4>{3.0}, true),
+      Real<4>{3.0} + std::ldexp(Real<4>{1.0}, -22));
+  EXPECT_EQ(RTNAME(Nearest8)(Real<8>{1.0}, true),
+      Real<8>{1.0} + std::ldexp(Real<8>{1.0}, -52));
+  EXPECT_EQ(RTNAME(Nearest8)(Real<8>{1.0}, false),
+      Real<8>{1.0} - std::ldexp(Real<8>{1.0}, -52));
+}
+
+TEST(Numeric, Nint) {
+  EXPECT_EQ(RTNAME(Nint4_4)(Real<4>{2.783}), 3);
+  EXPECT_EQ(RTNAME(Nint8_4)(Real<8>{-2.783}), -3);
+  EXPECT_EQ(RTNAME(Nint4_4)(Real<4>{2.5}), 3);
+  EXPECT_EQ(RTNAME(Nint8_4)(Real<8>{-2.5}), -3);
+  EXPECT_EQ(RTNAME(Nint8_8)(Real<8>{0}), 0);
+  EXPECT_EQ(RTNAME(Nint4_4)(std::numeric_limits<Real<4>>::infinity()),
+      std::numeric_limits<Int<4>>::min());
+  EXPECT_EQ(RTNAME(Nint4_4)(std::numeric_limits<Real<4>>::quiet_NaN()),
+      std::numeric_limits<Int<4>>::min());
+}
+
+TEST(Numeric, RRSpacing) {
+  EXPECT_EQ(RTNAME(RRSpacing8)(Real<8>{0}), 0);
+  EXPECT_EQ(RTNAME(RRSpacing4)(Real<4>{-3.0}), 0.75 * (1 << 24));
+  EXPECT_EQ(RTNAME(RRSpacing8)(Real<8>{-3.0}), 0.75 * (std::int64_t{1} << 53));
+  EXPECT_TRUE(
+      std::isnan(RTNAME(RRSpacing4)(std::numeric_limits<Real<4>>::infinity())));
+  EXPECT_TRUE(std::isnan(
+      RTNAME(RRSpacing8)(std::numeric_limits<Real<8>>::quiet_NaN())));
+}
+
+TEST(Numeric, Scale) {
+  EXPECT_EQ(RTNAME(Scale4)(Real<4>{0}, 0), 0);
+  EXPECT_EQ(RTNAME(Scale4)(Real<4>{1.0}, 0), 1.0);
+  EXPECT_EQ(RTNAME(Scale4)(Real<4>{1.0}, 1), 2.0);
+  EXPECT_EQ(RTNAME(Scale4)(Real<4>{1.0}, -1), 0.5);
+  EXPECT_TRUE(
+      std::isinf(RTNAME(Scale4)(std::numeric_limits<Real<4>>::infinity(), 1)));
+  EXPECT_TRUE(
+      std::isnan(RTNAME(Scale8)(std::numeric_limits<Real<8>>::quiet_NaN(), 1)));
+}
+
+TEST(Numeric, SetExponent) {
+  EXPECT_EQ(RTNAME(SetExponent4)(Real<4>{0}, 0), 0);
+  EXPECT_EQ(RTNAME(SetExponent8)(Real<8>{0}, 666), 0);
+  EXPECT_EQ(RTNAME(SetExponent8)(Real<8>{3.0}, 0), 1.5);
+  EXPECT_EQ(RTNAME(SetExponent4)(Real<4>{1.0}, 0), 1.0);
+  EXPECT_EQ(RTNAME(SetExponent4)(Real<4>{1.0}, 1), 2.0);
+  EXPECT_EQ(RTNAME(SetExponent4)(Real<4>{1.0}, -1), 0.5);
+  EXPECT_TRUE(std::isnan(
+      RTNAME(SetExponent4)(std::numeric_limits<Real<4>>::infinity(), 1)));
+  EXPECT_TRUE(std::isnan(
+      RTNAME(SetExponent8)(std::numeric_limits<Real<8>>::quiet_NaN(), 1)));
+}
+
+TEST(Numeric, Spacing) {
+  EXPECT_EQ(RTNAME(Spacing8)(Real<8>{0}), std::numeric_limits<Real<8>>::min());
+  EXPECT_EQ(RTNAME(Spacing4)(Real<4>{3.0}), std::ldexp(Real<4>{1.0}, -22));
+  EXPECT_TRUE(
+      std::isnan(RTNAME(Spacing4)(std::numeric_limits<Real<4>>::infinity())));
+  EXPECT_TRUE(
+      std::isnan(RTNAME(Spacing8)(std::numeric_limits<Real<8>>::quiet_NaN())));
+}