About:
* test_math: skip tests on math.fma(), math.hypot() and math.log2() which are known to fail on newlib C library (Cygwin).
On Cygwin, some of the test on the mathematical functions fail.
You can see this error on screen:
0:23:31 load avg: 0.64 [18/40/16] test_math failed (5 errors, 33 failures)
followed by a number of lines describing the failed tests.
Actually, the cause of all these errors is inside the implementation of the fma() math function.
This is an "hystorical" bug of Cygwin because NEWLIB provides a very minimalist implementation of this function and, for this reason, it doesn't guarantee the precision and it doesn't even provides a software replacement for this task:
https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;a=blob;f=newlib/libm/common/s_fma.c
I have seen that there is a macro called UNRELIABLE_FMA inside Modules\mathmodule.c.
I tried to activate this macro with a quick hack and the number of failures decreased from 33 to 20.
As you can see, it is still failing elsewhere, the above macro fixed some but not all the issues.
This happens because the fma() is used also for implementing other math functions provided by LIBM.
Affected functions are:
So, there is not much that is it could be done here, on the source code of Python.
However, besides the obvious solution of fixing that function inside LIBM, a possible way for fixing this malfunction is using a function from another C library.
At the end, Cygwin still runs on top of Windows.
And Windows has its own DLL libraries for providing C functions, including the math ones.
The fma() is not provided by MSVCRT, but it could be get from UCRT, as well as all other affected math functions.
The UCRT is available on all versions of Windows supported by Cygwin and it can be made available even on Windows XP by installing the Microsoft Visual C++ Redistributable.
So, I did a quick patch for testing and I imported those library functions from UCRT.
As result, all mathematical tests have been executed successfully.
See here my change:
diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c
index 6b7fc00..a83d8ea 100644
--- a/Modules/mathmodule.c
+++ b/Modules/mathmodule.c
@@ -114,6 +114,81 @@ dl_sum(double a, double b)
#ifndef UNRELIABLE_FMA
+#ifdef __CYGWIN__
+
+#define WIN32_LEN_AND_MEAN
+#define NOGDI
+#include <windows.h>
+
+#define CRT_NAME "ucrtbase"
+#define crt_abi __attribute__((ms_abi))
+
+typedef double (crt_abi *crt_fma_t)(double, double, double);
+typedef double (crt_abi *crt_pow_t)(double, double);
+typedef double (crt_abi *crt_log_t)(double);
+typedef double (crt_abi *crt_log2_t)(double);
+
+static void *crt_get_math_proc_ptr(const char *function_name)
+{
+ HMODULE crt_handle = GetModuleHandleA(CRT_NAME);
+
+ if (crt_handle) {
+ return (void *)GetProcAddress(crt_handle, function_name);
+ }
+ return NULL;
+}
+
+static double crt_fma(double a, double b, double c)
+{
+ crt_fma_t crt_fma = (crt_fma_t)crt_get_math_proc_ptr("fma");
+
+ if (crt_fma) {
+ return crt_fma(a, b, c);
+ }
+ return fma(a, b, c);
+}
+
+static double crt_pow(double a, double b)
+{
+ crt_pow_t crt_pow = (crt_pow_t)crt_get_math_proc_ptr("pow");
+
+ if (crt_pow) {
+ return crt_pow(a, b);
+ }
+ return pow(a, b);
+}
+
+static double crt_log(double a)
+{
+ crt_log_t crt_log = (crt_log_t)crt_get_math_proc_ptr("log");
+
+ if (crt_log) {
+ return crt_log(a);
+ }
+ return log(a);
+}
+
+static double crt_log2(double a)
+{
+ crt_log2_t crt_log2 = (crt_log2_t)crt_get_math_proc_ptr("log2");
+
+ if (crt_log2) {
+ return crt_log2(a);
+ }
+ return log2(a);
+}
+
+#undef fma
+#undef pow
+#undef log
+#undef log2
+#define fma crt_fma
+#define pow crt_pow
+#define log crt_log
+#define log2 crt_log2
+
+#endif
+
static DoubleLength
dl_mul(double x, double y)
{
As you can see, it is just a draft and there are surely better ways for optimizing it, perhaps by loading the pointer to functions only once and falling back to the ones provided by LIBM if they are not found for any reason, or by adding an additional crt_errno() if an error condition needs to be detected (which is not needed at the time of writing).
After running the test, this is the result on screen:
$ ./python.exe -m test -v test_math
== CPython 3.16.0a0 (heads/main-dirty:a5cb7c3, May 24 2026, 14:39:47) [GCC 13.4.0]
== CYGWIN_NT-10.0-26200-3.6.9-1.x86_64-x86_64-64bit-WindowsPE little-endian
== Python build: release shared
== cwd: /home/carlo/packages/python/cygwin/build/test_python_worker_1617æ
== CPU count: 16
== encodings: locale=UTF-8 FS=utf-8
== resources: all test resources are disabled, use -u option to unskip tests
Using random seed: 1958964126
0:00:00 load avg: 3.92 Run 1 test sequentially in a single process
0:00:00 load avg: 3.92 [1/1] test_math
test_fma_infinities (test.test_math.FMATests.test_fma_infinities) ... ok
test_fma_nan_results (test.test_math.FMATests.test_fma_nan_results) ... ok
test_fma_overflow (test.test_math.FMATests.test_fma_overflow) ... ok
test_fma_single_round (test.test_math.FMATests.test_fma_single_round) ... ok
test_fma_zero_result (test.test_math.FMATests.test_fma_zero_result) ... ok
test_random (test.test_math.FMATests.test_random) ... ok
test_asymmetry (test.test_math.IsCloseTests.test_asymmetry) ... ok
test_decimals (test.test_math.IsCloseTests.test_decimals) ... ok
test_eight_decimal_places (test.test_math.IsCloseTests.test_eight_decimal_places) ... ok
test_fractions (test.test_math.IsCloseTests.test_fractions) ... ok
test_identical (test.test_math.IsCloseTests.test_identical) ... ok
test_identical_infinite (test.test_math.IsCloseTests.test_identical_infinite) ... ok
test_inf_ninf_nan (test.test_math.IsCloseTests.test_inf_ninf_nan) ... ok
test_integers (test.test_math.IsCloseTests.test_integers) ... ok
test_near_zero (test.test_math.IsCloseTests.test_near_zero) ... ok
test_negative_tolerances (test.test_math.IsCloseTests.test_negative_tolerances) ... ok
test_zero_tolerance (test.test_math.IsCloseTests.test_zero_tolerance) ... ok
testAcos (test.test_math.MathTests.testAcos) ... ok
testAcosh (test.test_math.MathTests.testAcosh) ... ok
testAsin (test.test_math.MathTests.testAsin) ... ok
testAsinh (test.test_math.MathTests.testAsinh) ... ok
testAtan (test.test_math.MathTests.testAtan) ... ok
testAtan2 (test.test_math.MathTests.testAtan2) ... ok
testAtanh (test.test_math.MathTests.testAtanh) ... ok
testCbrt (test.test_math.MathTests.testCbrt) ... ok
testCeil (test.test_math.MathTests.testCeil) ... ok
testConstants (test.test_math.MathTests.testConstants) ... ok
testCopysign (test.test_math.MathTests.testCopysign) ... ok
testCos (test.test_math.MathTests.testCos) ... ok
testCosh (test.test_math.MathTests.testCosh) ... ok
testDegrees (test.test_math.MathTests.testDegrees) ... ok
testDist (test.test_math.MathTests.testDist) ... ok
testExp (test.test_math.MathTests.testExp) ... ok
testExp2 (test.test_math.MathTests.testExp2) ... ok
testFabs (test.test_math.MathTests.testFabs) ... ok
testFloor (test.test_math.MathTests.testFloor) ... ok
testFmod (test.test_math.MathTests.testFmod) ... ok
testFrexp (test.test_math.MathTests.testFrexp) ... ok
testFsum (test.test_math.MathTests.testFsum) ... ok
testGcd (test.test_math.MathTests.testGcd) ... ok
testHypot (test.test_math.MathTests.testHypot) ... ok
testHypotAccuracy (test.test_math.MathTests.testHypotAccuracy) ... ok
testIsfinite (test.test_math.MathTests.testIsfinite) ... ok
testIsinf (test.test_math.MathTests.testIsinf) ... ok
testIsnan (test.test_math.MathTests.testIsnan) ... ok
testIsnormal (test.test_math.MathTests.testIsnormal) ... ok
testIssubnormal (test.test_math.MathTests.testIssubnormal) ... ok
testLdexp (test.test_math.MathTests.testLdexp) ... ok
testLdexp_denormal (test.test_math.MathTests.testLdexp_denormal) ... ok
testLog (test.test_math.MathTests.testLog) ... ok
testLog10 (test.test_math.MathTests.testLog10) ... ok
testLog1p (test.test_math.MathTests.testLog1p) ... ok
testLog2 (test.test_math.MathTests.testLog2) ... ok
testLog2Exact (test.test_math.MathTests.testLog2Exact) ... ok
testModf (test.test_math.MathTests.testModf) ... ok
testPow (test.test_math.MathTests.testPow) ... ok
testRadians (test.test_math.MathTests.testRadians) ... ok
testRemainder (test.test_math.MathTests.testRemainder) ... ok
testSin (test.test_math.MathTests.testSin) ... ok
testSinh (test.test_math.MathTests.testSinh) ... ok
testSqrt (test.test_math.MathTests.testSqrt) ... ok
testSumProd (test.test_math.MathTests.testSumProd) ... ok
testTan (test.test_math.MathTests.testTan) ... ok
testTanh (test.test_math.MathTests.testTanh) ... ok
testTanhSign (test.test_math.MathTests.testTanhSign) ... ok
test_exception_messages (test.test_math.MathTests.test_exception_messages) ... ok
test_exceptions (test.test_math.MathTests.test_exceptions) ... ok
test_fmax (test.test_math.MathTests.test_fmax) ... ok
test_fmax_nans (test.test_math.MathTests.test_fmax_nans) ... ok
test_fmin (test.test_math.MathTests.test_fmin) ... ok
test_fmin_nans (test.test_math.MathTests.test_fmin_nans) ... ok
test_inf_constant (test.test_math.MathTests.test_inf_constant) ... ok
test_input_exceptions (test.test_math.MathTests.test_input_exceptions) ... ok
test_issue39871 (test.test_math.MathTests.test_issue39871) ... ok
test_lcm (test.test_math.MathTests.test_lcm) ... ok
test_log_huge_integer (test.test_math.MathTests.test_log_huge_integer) ... ok
test_math_dist_leak (test.test_math.MathTests.test_math_dist_leak) ... ok
test_mtestfile (test.test_math.MathTests.test_mtestfile) ... ok
test_nan_constant (test.test_math.MathTests.test_nan_constant) ... ok
test_nextafter (test.test_math.MathTests.test_nextafter) ... ok
test_prod (test.test_math.MathTests.test_prod) ... ok
test_signbit (test.test_math.MathTests.test_signbit) ... ok
test_sumprod_accuracy (test.test_math.MathTests.test_sumprod_accuracy) ... ok
test_sumprod_extended_precision_accuracy (test.test_math.MathTests.test_sumprod_extended_precision_accuracy) ... skipped "resource 'cpu' is not enabled"
test_sumprod_stress (test.test_math.MathTests.test_sumprod_stress) ... skipped "resource 'cpu' is not enabled"
test_testfile (test.test_math.MathTests.test_testfile) ... ok
test_trunc (test.test_math.MathTests.test_trunc) ... ok
test_ulp (test.test_math.MathTests.test_ulp) ... ok
/home/carlo/packages/python/cpython/Lib/test/mathdata/ieee754.txt
Doctest: ieee754.txt ... ok
----------------------------------------------------------------------
Ran 89 tests in 4.454s
OK (skipped=2)
0:00:05 load avg: 3.88 [1/1] test_math passed
== Tests result: SUCCESS ==
1 test OK.
Total duration: 5.2 sec
Total tests: run=89 skipped=2
Total test files: run=1/1
Result: SUCCESS
However... in my personal opinion, it would be better to fix LIBM, which would make the problem solved at the roots and it would be nice to have a direct support to FMA3 and FMA4 and a software implementation as fallback.
In that case, it must be also accepted the fact that older versions of Cygwin won't be able to execute the testsuite successfully, but this is quite obvious.
I'm providing this report as a reference for the Enhance Cygwin support topic.
EDIT: in my opinion, hypot() function is fine.
The tests were failing because the function to fail was sqrt() since it is a subset of the pow() function.