https://github.com/python/cpython/commit/e7ef46acf6c6da08925b183f5894c502bcd43faf
commit: e7ef46acf6c6da08925b183f5894c502bcd43faf
branch: main
author: Sergey B Kirpichev <[email protected]>
committer: vstinner <[email protected]>
date: 2026-09-01T15:16:56+02:00
summary:

gh-153740: Optimize PyFloat_Pack/Unpack2 using native _Float16 (#154796)

Co-authored-by: hpkfft.com <[email protected]>
Co-authored-by: Victor Stinner <[email protected]>

files:
A Misc/NEWS.d/next/C_API/2026-07-28-04-29-52.gh-issue-153740.7_LMSA.rst
M Lib/test/test_capi/test_float.py
M Objects/floatobject.c
M configure
M configure.ac
M pyconfig.h.in

diff --git a/Lib/test/test_capi/test_float.py b/Lib/test/test_capi/test_float.py
index 8b25607b6d504f7..91da2cf5ee9f616 100644
--- a/Lib/test/test_capi/test_float.py
+++ b/Lib/test/test_capi/test_float.py
@@ -226,6 +226,8 @@ def test_pack_unpack_roundtrip_for_nans(self):
                         value = unpack(data1, endian)
                         data2 = pack(size, value, endian)
                         self.assertTrue(math.isnan(value))
+                        self.assertEqual(math.copysign(1.0, value),
+                                         -1.0 if sign else 1.0)
                         self.assertEqual(data1, data2)
 
     @unittest.skipUnless(HAVE_IEEE_754, "requires IEEE 754")
diff --git 
a/Misc/NEWS.d/next/C_API/2026-07-28-04-29-52.gh-issue-153740.7_LMSA.rst 
b/Misc/NEWS.d/next/C_API/2026-07-28-04-29-52.gh-issue-153740.7_LMSA.rst
new file mode 100644
index 000000000000000..f01009f10e9b5b1
--- /dev/null
+++ b/Misc/NEWS.d/next/C_API/2026-07-28-04-29-52.gh-issue-153740.7_LMSA.rst
@@ -0,0 +1,3 @@
+If available on the platform, use native :c:type:`_Float16` in
+:c:func:`PyFloat_Pack2` and :c:func:`PyFloat_Unpack2` functions.  Patch by
+Sergey B Kirpichev.
diff --git a/Objects/floatobject.c b/Objects/floatobject.c
index 17e6a729dcd83fc..e379770e10031d2 100644
--- a/Objects/floatobject.c
+++ b/Objects/floatobject.c
@@ -1895,6 +1895,31 @@ int
 PyFloat_Pack2(double x, char *data, int le)
 {
     unsigned char *p = (unsigned char *)data;
+#if HAVE_FLOAT16
+    /* Conversion can change NaNs type or alter payload.  Here we
+       just fallback to the generic code, instead of providing
+       workarounds as for single/double precision. */
+    if (!isnan(x)) {
+        _Float16 y = (_Float16)x;
+
+        if (isinf(y) && !isinf(x)) {
+            goto Overflow;
+        }
+
+        unsigned char s[sizeof(_Float16)];
+
+        memcpy(s, &y, sizeof(_Float16));
+        if ((_PY_FLOAT_LITTLE_ENDIAN && !le) || (_PY_FLOAT_BIG_ENDIAN && le)) {
+            p[1] = s[0];
+            p[0] = s[1];
+        }
+        else {
+            p[0] = s[0];
+            p[1] = s[1];
+        }
+        return 0;
+    }
+#endif
     unsigned char sign;
     int e;
     double f;
@@ -2090,6 +2115,24 @@ double
 PyFloat_Unpack2(const char *data, int le)
 {
     unsigned char *p = (unsigned char *)data;
+#if HAVE_FLOAT16
+    _Float16 x16;
+
+    if ((_PY_FLOAT_LITTLE_ENDIAN && !le) || (_PY_FLOAT_BIG_ENDIAN && le)) {
+        char buf[2];
+
+        buf[1] = p[0];
+        buf[0] = p[1];
+        memcpy(&x16, buf, 2);
+    }
+    else {
+        memcpy(&x16, p, 2);
+    }
+    if (!isnan(x16)) {
+        return x16;
+    }
+    /* Fallback to the generic code for NaNs, see PyFloat_Pack2(). */
+#endif
     unsigned char sign;
     int e;
     unsigned int f;
diff --git a/configure b/configure
index 6b560fe6841b722..e0ad04b036aeb13 100755
--- a/configure
+++ b/configure
@@ -16724,6 +16724,65 @@ printf "%s\n" "#define _Py_FFI_SUPPORT_C_COMPLEX 1" 
>>confdefs.h
 
 fi
 
+# Check for native half-float type (_Float16).
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for _Float16 support" 
>&5
+printf %s "checking for _Float16 support... " >&6; }
+if test ${ac_cv_float16_supported+y}
+then :
+  printf %s "(cached) " >&6
+else case e in #(
+  e) save_CFLAGS=$CFLAGS
+save_CPPFLAGS=$CPPFLAGS
+save_LDFLAGS=$LDFLAGS
+save_LIBS=$LIBS
+
+
+CFLAGS="$CFLAGS -O0"
+if test "$cross_compiling" = yes
+then :
+  ac_cv_float16_supported=no
+else case e in #(
+  e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int main(void)
+{
+    _Float16 val = 1.0f16;
+    double d = 3.14;
+    val = d;
+    return 0;
+}
+
+_ACEOF
+if ac_fn_c_try_run "$LINENO"
+then :
+  ac_cv_float16_supported=yes
+else case e in #(
+  e) ac_cv_float16_supported=no ;;
+esac
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+  conftest.$ac_objext conftest.beam conftest.$ac_ext ;;
+esac
+fi
+
+CFLAGS=$save_CFLAGS
+CPPFLAGS=$save_CPPFLAGS
+LDFLAGS=$save_LDFLAGS
+LIBS=$save_LIBS
+
+ ;;
+esac
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: 
$ac_cv_float16_supported" >&5
+printf "%s\n" "$ac_cv_float16_supported" >&6; }
+if test "x$ac_cv_float16_supported" = xyes
+then :
+
+printf "%s\n" "#define HAVE_FLOAT16 1" >>confdefs.h
+
+fi
+
 
 pkg_failed=no
 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libmpdec >= 2.5.0" 
>&5
diff --git a/configure.ac b/configure.ac
index 476f13c82bbb2b9..2b8a5b052878c91 100644
--- a/configure.ac
+++ b/configure.ac
@@ -4469,6 +4469,25 @@ if test "$ac_cv_ffi_complex_double_supported" = "yes"; 
then
               [Defined if _Complex C type can be used with libffi.])
 fi
 
+# Check for native half-float type (_Float16).
+AC_CACHE_CHECK([for _Float16 support], [ac_cv_float16_supported],
+WITH_SAVE_ENV([
+CFLAGS="$CFLAGS -O0"
+AC_RUN_IFELSE([AC_LANG_SOURCE([[
+int main(void)
+{
+    _Float16 val = 1.0f16;
+    double d = 3.14;
+    val = d;
+    return 0;
+}
+]])], [ac_cv_float16_supported=yes],
+[ac_cv_float16_supported=no],
+[ac_cv_float16_supported=no])]))
+AS_VAR_IF([ac_cv_float16_supported], [yes],
+          [AC_DEFINE([HAVE_FLOAT16], [1],
+                     [Defined if _Float16 C type is supported])])
+
 dnl Check for libmpdec >= 2.5.0
 PKG_CHECK_MODULES([LIBMPDEC], [libmpdec >= 2.5.0], [have_mpdec=yes], [
   WITH_SAVE_ENV([
diff --git a/pyconfig.h.in b/pyconfig.h.in
index 64b5c52790b4581..c2a36afe334763c 100644
--- a/pyconfig.h.in
+++ b/pyconfig.h.in
@@ -497,6 +497,9 @@
 /* Define if you have the 'ffi_prep_closure_loc' function. */
 #undef HAVE_FFI_PREP_CLOSURE_LOC
 
+/* Defined if _Float16 C type is supported */
+#undef HAVE_FLOAT16
+
 /* Define to 1 if you have the 'flock' function. */
 #undef HAVE_FLOCK
 

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to