Hello, I have deeply looked at this issue and found more issues in
mingw-w64 code. There are more problems which this test suite uncovered
and one is even not in the feenableexcept/fedisableexcept/fegetexcept
code.


First thing what I have already found (and sent) is that the
fegetexcept() has to return bits of enabled exceptions (not disabled).
Hence the return value needs to be negated:

diff --git a/mingw-w64-crt/misc/fegetexcept.c b/mingw-w64-crt/misc/fegetexcept.c
index ced9f50b0e65..6bbf8d1bde8b 100644
--- a/mingw-w64-crt/misc/fegetexcept.c
+++ b/mingw-w64-crt/misc/fegetexcept.c
@@ -4,5 +4,5 @@

 int __cdecl fegetexcept(void)
 {
-  return __mingw_controlfp(0, 0) & FE_ALL_EXCEPT;
+  return ~__mingw_controlfp(0, 0) & FE_ALL_EXCEPT;
 }

This fixes all those test-fenv-env-* tests.


Second thing is broken return value of feenableexcept() and
fedisableexcept(). They have to return bits of OLD enabled exceptions
(not new after change). The __mingw_controlfp is returning new bits.
So the fix for those functions should be something like:

diff --git a/mingw-w64-crt/misc/fedisableexcept.c 
b/mingw-w64-crt/misc/fedisableexcept.c
index 29b905e3d84f..5c3328d02c3b 100644
--- a/mingw-w64-crt/misc/fedisableexcept.c
+++ b/mingw-w64-crt/misc/fedisableexcept.c
@@ -4,5 +4,8 @@

 int __cdecl fedisableexcept(int excepts)
 {
-  return __mingw_controlfp(excepts & FE_ALL_EXCEPT, excepts & FE_ALL_EXCEPT) & 
FE_ALL_EXCEPT;
+  if (excepts & ~FE_ALL_EXCEPT) return -1;
+  int old_excepts = fegetexcept();
+  __mingw_controlfp(excepts, excepts);
+  return old_excepts;
 }
diff --git a/mingw-w64-crt/misc/feenableexcept.c 
b/mingw-w64-crt/misc/feenableexcept.c
index 8a40bd5fdc6b..7bd6f757fc07 100644
--- a/mingw-w64-crt/misc/feenableexcept.c
+++ b/mingw-w64-crt/misc/feenableexcept.c
@@ -4,5 +4,8 @@

 int __cdecl feenableexcept(int excepts)
 {
-  return __mingw_controlfp(0, excepts & FE_ALL_EXCEPT) & FE_ALL_EXCEPT;
+  if (excepts & ~FE_ALL_EXCEPT) return -1;
+  int old_excepts = fegetexcept();
+  __mingw_controlfp(0, excepts);
+  return old_excepts;
 }

This fixes part of the test-fenv-except-trapping-1 but not all.


The third thing is that trying to enable already enabled exception or
trying to disable already disabled exception cause disabling of all
exceptions. It look me some time to figure out how why it happens and
the issue is in the __mingw_setfp function which modifies x87 cw reg.
The problem is that the current implementation for changing register to
the same value cause disabled of all exception interrupts. This is
caused by usage of FSTENV/FNSTENV instruction which automatically
disable all exceptions. The fix is to always call FLDENV if the
FSTENV/FNSTENV was called, so it would restore the exceptions (either
old unchanged, or new one which should be changed).

diff --git a/mingw-w64-crt/misc/mingw_setfp.c b/mingw-w64-crt/misc/mingw_setfp.c
index dcd767d9ab5c..41768b29e94c 100644
--- a/mingw-w64-crt/misc/mingw_setfp.c
+++ b/mingw-w64-crt/misc/mingw_setfp.c
@@ -121,6 +121,7 @@ void __mingw_setfp( unsigned int *cw, unsigned int cw_mask,
     unsigned long oldcw = 0, newcw = 0;
     unsigned long oldsw = 0, newsw = 0;
     unsigned int flags;
+    int use_fnstenv_fldenv;
     struct {
         WORD control_word;
         WORD unused1;
@@ -139,7 +140,9 @@ void __mingw_setfp( unsigned int *cw, unsigned int cw_mask,
     cw_mask &= _MCW_EM | _MCW_IC | _MCW_RC | _MCW_PC;
     sw_mask &= _MCW_EM;
 
-    if ((!sw || sw_mask == 0) && (!cw || cw_mask == 0))
+    use_fnstenv_fldenv = ((sw && sw_mask != 0) || (cw && cw_mask != 0));
+
+    if (!use_fnstenv_fldenv)
     {
         /* Fast path: when we are not going to change sw/cw which is indicated
          * by zero mask then load sw/cw via fast fnstsw/fnstcw instruction.
@@ -151,6 +154,9 @@ void __mingw_setfp( unsigned int *cw, unsigned int cw_mask,
     {
         /* Slow path: when we are going to change sw/cw or we do not know yet 
then
          * load whole x87 env via slow fnstenv as it is needed for changing 
sw/cw.
+         * Note that fnstenv masks all floating-point exceptions after storing 
the
+         * x87 env. And after the fnstenv call, it is always required to 
restore
+         * masking of previous floating-point exceptions via the fldenv call.
          */
         __asm__ __volatile__( "fnstenv %0" : "=m" (fenv) );
         newsw = fenv.status_word;
@@ -229,8 +235,10 @@ void __mingw_setfp( unsigned int *cw, unsigned int cw_mask,
 
     /* For changing sw/cw always use fldenv.
      * Do not use fldcw as it can generate pending floating-point exception.
+     * When the fnstenv was called then it is required to call fldenv to
+     * restore previous floating-point exceptions.
      */
-    if (oldsw != newsw || oldcw != newcw)
+    if (use_fnstenv_fldenv)
     {
         fenv.control_word = newcw;
         fenv.status_word = newsw;


The last issue is not related to feenableexcept/fedisableexcept but
rather to whole fenv. It was just uncovered by the fedisableexcept test
because seems it is the only one which is doing thing like "set new
value to the same one", and this test requires the fedisableexcept
implementation.


With all these 3 mingw-w64 changes, those gltests tests are passing.

I will send these mingw-w64 changes to the mingw-w64 list for review.


For improving debugging of gltests fenv tests, I would suggest to extend
test-fenv-except-trapping-1.c test with additional assert, which will
check that the feenableexcept() call correctly enabled the exception "a":

--- gltests/test-fenv-except-trapping-1.c       2026-04-06 16:13:27.000000000 
+0200
+++ gltests/test-fenv-except-trapping-1.c       2026-04-11 00:01:49.297039481 
+0200
@@ -60,6 +60,7 @@ main ()
             fputs ("Skipping test: trapping floating-point exceptions are not 
supported on this machine.\n", stderr);
             return 77;
           }
+        ASSERT (fegetexcept () == uint_to_exceptions (a));
         ASSERT (fedisableexcept (uint_to_exceptions (b))
                 == uint_to_exceptions (a));
         /* Check fegetexcept.  */


Hopefully this explains everything these and address all findings and
makes the mingw-w64 code compatible.

Pali

On Monday 30 March 2026 22:33:26 Bruno Haible wrote:
> While the <fenv.h> changes in mingw 13.0.0 are working all right,
> in mingw 14.0.0 they added broken implementations of feenableexcept()
> and fedisableexcept(). This leads to Gnulib test suite failures:
> 
> $ ./gnulib-tool --create-testdir --dir=../testdir1 \
>                 fenv-environment 
> fenv-exceptitest-fenv-except-trapping-1ons-state-c23 \
>                 fenv-exceptions-tracking-c23 fenv-exceptions-trapping \
>                 fenv-rounding
> 
> =>
> 
> FAIL: test-fenv-env-2
> =====================
> 
> ../../gltests/test-fenv-env-2.c:70: assertion 'fegetexcept () == 
> (supports_trapping ? FE_DIVBYZERO : 0)' failed
> FAIL test-fenv-env-2.exe (exit status: 3)
> 
> FAIL: test-fenv-env-3
> =====================
> 
> ../../gltests/test-fenv-env-3.c:70: assertion 'fegetexcept () == 
> (supports_trapping ? FE_DIVBYZERO : 0)' failed
> FAIL test-fenv-env-3.exe (exit status: 3)
> 
> FAIL: test-fenv-env-4
> =====================
> 
> ../../gltests/test-fenv-env-4.c:67: assertion 'fegetexcept () == 0' failed
> FAIL test-fenv-env-4.exe (exit status: 3)
> 
> FAIL: test-fenv-env-5
> =====================
> 
> ../../gltests/test-fenv-env-5.c:67: assertion 'fegetexcept () == 0' failed
> FAIL test-fenv-env-5.exe (exit status: 3)
> 
> FAIL: test-fenv-except-trapping-1
> =================================
> 
> ../../gltests/test-fenv-except-trapping-1.c:63: assertion 'fedisableexcept 
> (uint_to_exceptions (b)) == uint_to_exceptions (a)' failed
> FAIL test-fenv-except-trapping-1.exe (exit status: 3)
> 
> 
> This patch adds a workaround: it overrides these broken implementations on
> mingw.
> 
> 
> 2026-03-30  Bruno Haible  <[email protected]>
> 
>       fenv-exceptions-trapping: Work around mingw 14.0.0 bug.
>       * m4/fenv-exceptions-trapping.m4 (gl_FENV_EXCEPTIONS_TRAPPING): Override
>       feenableexcept, fedisableexcept also on mingw.
> 
> diff --git a/m4/fenv-exceptions-trapping.m4 b/m4/fenv-exceptions-trapping.m4
> index dc388de27b..3679734775 100644
> --- a/m4/fenv-exceptions-trapping.m4
> +++ b/m4/fenv-exceptions-trapping.m4
> @@ -1,5 +1,5 @@
>  # fenv-exceptions-trapping.m4
> -# serial 3
> +# serial 4
>  dnl Copyright (C) 2023-2026 Free Software Foundation, Inc.
>  dnl This file is free software; the Free Software Foundation
>  dnl gives unlimited permission to copy and/or distribute it,
> @@ -20,6 +20,7 @@ AC_DEFUN_ONCE([gl_FENV_EXCEPTIONS_TRAPPING]
>      dnl Fixed through
>      dnl 
> <https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=302949e2940a9da3f6364a1574619e621b7e1e71>.
>      dnl Similarly on FreeBSD 12.2/arm, FreeBSD 12.2/arm64, NetBSD 10.0/arm64.
> +    dnl On mingw 14.0.0, feenableexcept and fedisableexcept are broken as 
> well.
>      case "$host" in
>        aarch64*-*-linux*)
>          AC_CACHE_CHECK([whether feenableexcept works],
> @@ -43,7 +44,8 @@ AC_DEFUN_ONCE([gl_FENV_EXCEPTIONS_TRAPPING]
>            *) REPLACE_FEENABLEEXCEPT=1 ;;
>          esac
>          ;;
> -      arm*-*-freebsd* | aarch64*-*-freebsd* | aarch64*-*-netbsd*)
> +      arm*-*-freebsd* | aarch64*-*-freebsd* | aarch64*-*-netbsd* | \
> +      *-*-mingw* | *-*-windows*)
>          REPLACE_FEENABLEEXCEPT=1
>          ;;
>      esac
> 
> 
> 

Reply via email to