[7/7] stdio-common: Skip the a and A conversions for the IBM extended format

Message ID 20260824221612.4132669-8-mattst88@gmail.com (mailing list archive)
State Committed
Commit 548997eb13ff3a9ad0394357bbdb8ea10b2a6cfb
Delegated to: Adhemerval Zanella Netto
Headers
Series stdio-common: Verify printf format tests with Python rather than AWK |

Checks

Context Check Description
redhat-pt-bot/TryBot-apply_patch success Patch applied to master at the time it was sent
linaro-tcwg-bot/tcwg_glibc_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_glibc_check--master-arm success Test passed
linaro-tcwg-bot/tcwg_glibc_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_glibc_check--master-aarch64 success Test passed
redhat-pt-bot/TryBot-32bit success Build for i686

Commit Message

Matt Turner Aug. 24, 2026, 10:16 p.m. UTC
  The reference implementation renders the a and A conversions by
splitting the value into a significand of the width the type has and a
power of two. That presumes the type is a single binary floating-point
format.  The IBM extended format is a pair of doubles instead, whose
combined significand has no fixed width and whose subnormals are those
of the low double rather than of the type, so what glibc produces for it
does not follow from PREC and MINEXP: LDBL_MAX comes out as
0x1.fffffffffffff7ffffffffffff8p+1023 with twenty-eight fractional
digits where the value has at most twenty-six worth of significand, and
LDBL_TRUE_MIN as 0x0.0000000000001p-1022, which is the smallest
subnormal double and nowhere near LDBL_MIN_EXP.

The remaining conversions are unaffected, as they work from the value
rather than from a decomposition of it, and were verified to be.

Add an UNSUPPORTED_CONVS definition to the skeleton for conversions that
cannot be modeled for the type at hand, and set it to the a and A pair
where long double has that format, the generator then producing no
records and an unsupported status which the long double wrapper reports.

This does not come up in practice: powerpc64le is the one target with
the format, and it builds everything with the IEEE format instead, so
the test programs there see a 113 bit significand.  That is done for
shared builds only though, which leaves the conversions failing where
shared libraries have been turned off.

Tested on x86_64-linux-gnu and powerpc64le-linux-gnu, where all 672
results pass, and with the long double conversions forced to the IBM
extended format, where the a and A ones report unsupported and the rest
continue to pass.
---
 stdio-common/tst-printf-format-ldouble.sh         | 13 +++++++++++--
 stdio-common/tst-printf-format-skeleton-ldouble.c |  9 +++++++++
 stdio-common/tst-printf-format-skeleton.c         | 15 ++++++++++++++-
 3 files changed, 34 insertions(+), 3 deletions(-)
  

Comments

Adhemerval Zanella Netto Sept. 2, 2026, 5:22 p.m. UTC | #1
On 24/08/26 19:16, Matt Turner wrote:
> The reference implementation renders the a and A conversions by
> splitting the value into a significand of the width the type has and a
> power of two. That presumes the type is a single binary floating-point
> format.  The IBM extended format is a pair of doubles instead, whose
> combined significand has no fixed width and whose subnormals are those
> of the low double rather than of the type, so what glibc produces for it
> does not follow from PREC and MINEXP: LDBL_MAX comes out as
> 0x1.fffffffffffff7ffffffffffff8p+1023 with twenty-eight fractional
> digits where the value has at most twenty-six worth of significand, and
> LDBL_TRUE_MIN as 0x0.0000000000001p-1022, which is the smallest
> subnormal double and nowhere near LDBL_MIN_EXP.
> 
> The remaining conversions are unaffected, as they work from the value
> rather than from a decomposition of it, and were verified to be.
> 
> Add an UNSUPPORTED_CONVS definition to the skeleton for conversions that
> cannot be modeled for the type at hand, and set it to the a and A pair
> where long double has that format, the generator then producing no
> records and an unsupported status which the long double wrapper reports.
> 
> This does not come up in practice: powerpc64le is the one target with
> the format, and it builds everything with the IEEE format instead, so
> the test programs there see a 113 bit significand.  

The IBM extended precision is also used on powerpc32 and powerpc64 (where
it is the default), and still the default ABI on powerpc64le:

* cfarm121 (powerpc32/powerpc64 BE ELFv1):

$ cat /etc/debian_version
forky/sid
$ echo | gcc -E -dM -xc - | grep -E 'LDBL_MANT_DIG|LONG_DOUBLE'
#define __LDBL_MANT_DIG__ 106
#define __SIZEOF_LONG_DOUBLE__ 16
#define __LONG_DOUBLE_IBM128__ 1
#define __LONG_DOUBLE_128__ 1
$ gcc -v 2>&1 | tr ' ' '\n' | grep -i long-double
--with-long-double-128


* cfarm120 (powerpc64le LE ELFv2):

$ cat /etc/redhat-release
AlmaLinux release 9.8 (Olive Jaguar)
$ echo | gcc -E -dM -xc - | grep -E 'LDBL_MANT_DIG|LONG_DOUBLE'
#define __LDBL_MANT_DIG__ 106
#define __SIZEOF_LONG_DOUBLE__ 16
#define __LONG_DOUBLE_IBM128__ 1
#define __LONG_DOUBLE_128__ 1
$ gcc -v 2>&1 | tr ' ' '\n' | grep -i long-double
--with-long-double-128

The last powerpc64le ABI did 'phase-in' the IEEE Binary 128 [1], and
it is fully supported on gcc (and for -mcpu=power9 it generates 
proper VSX instruction without the need to call libgcc). But unless
you explicit configure gcc to default for this format you will need
to use '-mabi=ieeelongdouble' to enable it.

I think it would be better to remove this paragraph.

[1] https://openpowerfoundation.org/specifications/64bitelfabi/

That is done for
> shared builds only though, which leaves the conversions failing where
> shared libraries have been turned off.
> 
> Tested on x86_64-linux-gnu and powerpc64le-linux-gnu, where all 672
> results pass, and with the long double conversions forced to the IBM
> extended format, where the a and A ones report unsupported and the rest
> continue to pass.

The rest look ok, thanks.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org)

> ---
>  stdio-common/tst-printf-format-ldouble.sh         | 13 +++++++++++--
>  stdio-common/tst-printf-format-skeleton-ldouble.c |  9 +++++++++
>  stdio-common/tst-printf-format-skeleton.c         | 15 ++++++++++++++-
>  3 files changed, 34 insertions(+), 3 deletions(-)
> 
> diff --git ./stdio-common/tst-printf-format-ldouble.sh ./stdio-common/tst-printf-format-ldouble.sh
> index 2be2a592e0..b62296b11c 100644
> --- ./stdio-common/tst-printf-format-ldouble.sh
> +++ ./stdio-common/tst-printf-format-ldouble.sh
> @@ -26,13 +26,22 @@ test_program_prefix=$1; shift
>  
>  status=0
>  
> -echo Verifying $format
> +rc=0
>  (set -o pipefail
>   ${test_program_prefix} \
>    ${common_objpfx}stdio-common/tst-printf-format-${xprintf}-ldouble $format |
>     ${PYTHON:-python3} tst-printf-format.py 2>&1 |
>     head -n 1 |
>     sed "s/^/Conversion $format output error, first line:\n/") 2>&1 ||
> -  status=1
> +  rc=$?
> +
> +# The generator produces no records and reports an unsupported status for
> +# a conversion the verification cannot model for the long double format in
> +# use; see UNSUPPORTED_CONVS in tst-printf-format-skeleton-ldouble.c.
> +case $rc in
> +0)  echo Verifying $format ;;
> +77) echo Unsupported $format; status=77 ;;
> +*)  echo Verifying $format; status=1 ;;
> +esac
>  
>  exit $status

Ok.

> diff --git ./stdio-common/tst-printf-format-skeleton-ldouble.c ./stdio-common/tst-printf-format-skeleton-ldouble.c
> index 0c828ec3b5..860b0ace7c 100644
> --- ./stdio-common/tst-printf-format-skeleton-ldouble.c
> +++ ./stdio-common/tst-printf-format-skeleton-ldouble.c
> @@ -28,6 +28,15 @@
>  #define REF_VAL(v) (v)
>  #define PREC LDBL_MANT_DIG
>  #define MINEXP LDBL_MIN_EXP
> +#if LDBL_MANT_DIG == 106
> +/* The IBM extended format is a pair of doubles rather than a significand
> +   of a single fixed width, so what the a and A conversions produce for it
> +   does not follow from PREC and MINEXP the way the verification assumes.
> +   Leave them out where the type has that format; the one target concerned
> +   builds everything with the IEEE format instead, so this only comes up
> +   where that has been turned off.  */
> +# define UNSUPPORTED_CONVS "aA"
> +#endif
>  typedef long double type_t;
>  static const type_t vals[] =
>    { -HUGE_VAL, -LDBL_MAX, -LDBL_MIN, -0.0, -NAN, NAN, 0, LDBL_TRUE_MIN,

Ok.

> diff --git ./stdio-common/tst-printf-format-skeleton.c ./stdio-common/tst-printf-format-skeleton.c
> index 6250792d44..ab93e3f654 100644
> --- ./stdio-common/tst-printf-format-skeleton.c
> +++ ./stdio-common/tst-printf-format-skeleton.c
> @@ -41,6 +41,10 @@
>     MINEXP	[optional] Minimum exponent integer constant.  Set to the
>  		*_MIN_EXP value for the argument type handled, so that
>  		subnormal values can be told apart from normal ones.
> +   UNSUPPORTED_CONVS
> +		[optional] String of conversions the verification cannot
> +		model for the argument type handled.  Asking for one of
> +		these produces no records and an unsupported status.
>  
>     Typedefs:
>     type_t	Variadic function argument type.  Define to the promoted
> @@ -67,6 +71,7 @@
>  #include <stdio.h>
>  #include <stdlib.h>
>  #include <string.h>
> +#include <support/test-driver.h>
>  
>  /* Set to nonzero to select all possible tuples with repetitions of 1..n
>     elements from the set of flags as defined in FLAGS array below; n is
> @@ -87,6 +92,11 @@
>  #ifndef MINEXP
>  # define MINEXP 0
>  #endif
> +/* Set to the conversions that cannot be verified for the type handled;
> +   empty where they all can be, which is the usual case.  */
> +#ifndef UNSUPPORTED_CONVS
> +# define UNSUPPORTED_CONVS ""
> +#endif
>  
>  /* The list of conversions permitted for the '#' flag, the '0' flag,
>     and precision respectively.  */
> @@ -333,6 +343,10 @@ do_test (int argc, char *argv[])
>        return EXIT_FAILURE;
>      }
>  
> +  c = *argv[1];
> +  if (strchr (UNSUPPORTED_CONVS, c) != NULL)
> +    return EXIT_UNSUPPORTED;
> +
>    mtrace ();
>  
>    if (PREC != 0 && printf ("prec:%i\n", PREC) < 0)
> @@ -347,7 +361,6 @@ do_test (int argc, char *argv[])
>        return EXIT_FAILURE;
>      }
>  
> -  c = *argv[1];
>    for (v = 0; v < array_length (vals); v++)
>      {
>        if (printf ("val:%" REF_FMT "\n", REF_VAL (vals[v])) < 0)


Ok.
  
Matt Turner Sept. 3, 2026, 3:24 a.m. UTC | #2
On Wed, Sep 2, 2026 at 1:22 PM Adhemerval Zanella Netto
<adhemerval.zanella@linaro.org> wrote:
>
>
>
> On 24/08/26 19:16, Matt Turner wrote:
> > The reference implementation renders the a and A conversions by
> > splitting the value into a significand of the width the type has and a
> > power of two. That presumes the type is a single binary floating-point
> > format.  The IBM extended format is a pair of doubles instead, whose
> > combined significand has no fixed width and whose subnormals are those
> > of the low double rather than of the type, so what glibc produces for it
> > does not follow from PREC and MINEXP: LDBL_MAX comes out as
> > 0x1.fffffffffffff7ffffffffffff8p+1023 with twenty-eight fractional
> > digits where the value has at most twenty-six worth of significand, and
> > LDBL_TRUE_MIN as 0x0.0000000000001p-1022, which is the smallest
> > subnormal double and nowhere near LDBL_MIN_EXP.
> >
> > The remaining conversions are unaffected, as they work from the value
> > rather than from a decomposition of it, and were verified to be.
> >
> > Add an UNSUPPORTED_CONVS definition to the skeleton for conversions that
> > cannot be modeled for the type at hand, and set it to the a and A pair
> > where long double has that format, the generator then producing no
> > records and an unsupported status which the long double wrapper reports.
> >
> > This does not come up in practice: powerpc64le is the one target with
> > the format, and it builds everything with the IEEE format instead, so
> > the test programs there see a 113 bit significand.
>
> The IBM extended precision is also used on powerpc32 and powerpc64 (where
> it is the default), and still the default ABI on powerpc64le:
>
> * cfarm121 (powerpc32/powerpc64 BE ELFv1):
>
> $ cat /etc/debian_version
> forky/sid
> $ echo | gcc -E -dM -xc - | grep -E 'LDBL_MANT_DIG|LONG_DOUBLE'
> #define __LDBL_MANT_DIG__ 106
> #define __SIZEOF_LONG_DOUBLE__ 16
> #define __LONG_DOUBLE_IBM128__ 1
> #define __LONG_DOUBLE_128__ 1
> $ gcc -v 2>&1 | tr ' ' '\n' | grep -i long-double
> --with-long-double-128
>
>
> * cfarm120 (powerpc64le LE ELFv2):
>
> $ cat /etc/redhat-release
> AlmaLinux release 9.8 (Olive Jaguar)
> $ echo | gcc -E -dM -xc - | grep -E 'LDBL_MANT_DIG|LONG_DOUBLE'
> #define __LDBL_MANT_DIG__ 106
> #define __SIZEOF_LONG_DOUBLE__ 16
> #define __LONG_DOUBLE_IBM128__ 1
> #define __LONG_DOUBLE_128__ 1
> $ gcc -v 2>&1 | tr ' ' '\n' | grep -i long-double
> --with-long-double-128
>
> The last powerpc64le ABI did 'phase-in' the IEEE Binary 128 [1], and
> it is fully supported on gcc (and for -mcpu=power9 it generates
> proper VSX instruction without the need to call libgcc). But unless
> you explicit configure gcc to default for this format you will need
> to use '-mabi=ieeelongdouble' to enable it.
>
> I think it would be better to remove this paragraph.
>
> [1] https://openpowerfoundation.org/specifications/64bitelfabi/
>
> That is done for
> > shared builds only though, which leaves the conversions failing where
> > shared libraries have been turned off.
> >
> > Tested on x86_64-linux-gnu and powerpc64le-linux-gnu, where all 672
> > results pass, and with the long double conversions forced to the IBM
> > extended format, where the a and A ones report unsupported and the rest
> > continue to pass.
>
> The rest look ok, thanks.
>
> Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org)

Thank you for all the reviews! I really appreciate it. Would you like
me to respin the series or would you prefer to make the change locally
yourself before pushing?

Thanks,
Matt
  
Adhemerval Zanella Netto Sept. 3, 2026, 11:31 a.m. UTC | #3
On 03/09/26 00:24, Matt Turner wrote:
> On Wed, Sep 2, 2026 at 1:22 PM Adhemerval Zanella Netto
> <adhemerval.zanella@linaro.org> wrote:
>>
>>
>>
>> On 24/08/26 19:16, Matt Turner wrote:
>>> The reference implementation renders the a and A conversions by
>>> splitting the value into a significand of the width the type has and a
>>> power of two. That presumes the type is a single binary floating-point
>>> format.  The IBM extended format is a pair of doubles instead, whose
>>> combined significand has no fixed width and whose subnormals are those
>>> of the low double rather than of the type, so what glibc produces for it
>>> does not follow from PREC and MINEXP: LDBL_MAX comes out as
>>> 0x1.fffffffffffff7ffffffffffff8p+1023 with twenty-eight fractional
>>> digits where the value has at most twenty-six worth of significand, and
>>> LDBL_TRUE_MIN as 0x0.0000000000001p-1022, which is the smallest
>>> subnormal double and nowhere near LDBL_MIN_EXP.
>>>
>>> The remaining conversions are unaffected, as they work from the value
>>> rather than from a decomposition of it, and were verified to be.
>>>
>>> Add an UNSUPPORTED_CONVS definition to the skeleton for conversions that
>>> cannot be modeled for the type at hand, and set it to the a and A pair
>>> where long double has that format, the generator then producing no
>>> records and an unsupported status which the long double wrapper reports.
>>>
>>> This does not come up in practice: powerpc64le is the one target with
>>> the format, and it builds everything with the IEEE format instead, so
>>> the test programs there see a 113 bit significand.
>>
>> The IBM extended precision is also used on powerpc32 and powerpc64 (where
>> it is the default), and still the default ABI on powerpc64le:
>>
>> * cfarm121 (powerpc32/powerpc64 BE ELFv1):
>>
>> $ cat /etc/debian_version
>> forky/sid
>> $ echo | gcc -E -dM -xc - | grep -E 'LDBL_MANT_DIG|LONG_DOUBLE'
>> #define __LDBL_MANT_DIG__ 106
>> #define __SIZEOF_LONG_DOUBLE__ 16
>> #define __LONG_DOUBLE_IBM128__ 1
>> #define __LONG_DOUBLE_128__ 1
>> $ gcc -v 2>&1 | tr ' ' '\n' | grep -i long-double
>> --with-long-double-128
>>
>>
>> * cfarm120 (powerpc64le LE ELFv2):
>>
>> $ cat /etc/redhat-release
>> AlmaLinux release 9.8 (Olive Jaguar)
>> $ echo | gcc -E -dM -xc - | grep -E 'LDBL_MANT_DIG|LONG_DOUBLE'
>> #define __LDBL_MANT_DIG__ 106
>> #define __SIZEOF_LONG_DOUBLE__ 16
>> #define __LONG_DOUBLE_IBM128__ 1
>> #define __LONG_DOUBLE_128__ 1
>> $ gcc -v 2>&1 | tr ' ' '\n' | grep -i long-double
>> --with-long-double-128
>>
>> The last powerpc64le ABI did 'phase-in' the IEEE Binary 128 [1], and
>> it is fully supported on gcc (and for -mcpu=power9 it generates
>> proper VSX instruction without the need to call libgcc). But unless
>> you explicit configure gcc to default for this format you will need
>> to use '-mabi=ieeelongdouble' to enable it.
>>
>> I think it would be better to remove this paragraph.
>>
>> [1] https://openpowerfoundation.org/specifications/64bitelfabi/
>>
>> That is done for
>>> shared builds only though, which leaves the conversions failing where
>>> shared libraries have been turned off.
>>>
>>> Tested on x86_64-linux-gnu and powerpc64le-linux-gnu, where all 672
>>> results pass, and with the long double conversions forced to the IBM
>>> extended format, where the a and A ones report unsupported and the rest
>>> continue to pass.
>>
>> The rest look ok, thanks.
>>
>> Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org)
> 
> Thank you for all the reviews! I really appreciate it. Would you like
> me to respin the series or would you prefer to make the change locally
> yourself before pushing?
I can change it locally, I will install this shortly.
  

Patch

diff --git ./stdio-common/tst-printf-format-ldouble.sh ./stdio-common/tst-printf-format-ldouble.sh
index 2be2a592e0..b62296b11c 100644
--- ./stdio-common/tst-printf-format-ldouble.sh
+++ ./stdio-common/tst-printf-format-ldouble.sh
@@ -26,13 +26,22 @@  test_program_prefix=$1; shift
 
 status=0
 
-echo Verifying $format
+rc=0
 (set -o pipefail
  ${test_program_prefix} \
   ${common_objpfx}stdio-common/tst-printf-format-${xprintf}-ldouble $format |
    ${PYTHON:-python3} tst-printf-format.py 2>&1 |
    head -n 1 |
    sed "s/^/Conversion $format output error, first line:\n/") 2>&1 ||
-  status=1
+  rc=$?
+
+# The generator produces no records and reports an unsupported status for
+# a conversion the verification cannot model for the long double format in
+# use; see UNSUPPORTED_CONVS in tst-printf-format-skeleton-ldouble.c.
+case $rc in
+0)  echo Verifying $format ;;
+77) echo Unsupported $format; status=77 ;;
+*)  echo Verifying $format; status=1 ;;
+esac
 
 exit $status
diff --git ./stdio-common/tst-printf-format-skeleton-ldouble.c ./stdio-common/tst-printf-format-skeleton-ldouble.c
index 0c828ec3b5..860b0ace7c 100644
--- ./stdio-common/tst-printf-format-skeleton-ldouble.c
+++ ./stdio-common/tst-printf-format-skeleton-ldouble.c
@@ -28,6 +28,15 @@ 
 #define REF_VAL(v) (v)
 #define PREC LDBL_MANT_DIG
 #define MINEXP LDBL_MIN_EXP
+#if LDBL_MANT_DIG == 106
+/* The IBM extended format is a pair of doubles rather than a significand
+   of a single fixed width, so what the a and A conversions produce for it
+   does not follow from PREC and MINEXP the way the verification assumes.
+   Leave them out where the type has that format; the one target concerned
+   builds everything with the IEEE format instead, so this only comes up
+   where that has been turned off.  */
+# define UNSUPPORTED_CONVS "aA"
+#endif
 typedef long double type_t;
 static const type_t vals[] =
   { -HUGE_VAL, -LDBL_MAX, -LDBL_MIN, -0.0, -NAN, NAN, 0, LDBL_TRUE_MIN,
diff --git ./stdio-common/tst-printf-format-skeleton.c ./stdio-common/tst-printf-format-skeleton.c
index 6250792d44..ab93e3f654 100644
--- ./stdio-common/tst-printf-format-skeleton.c
+++ ./stdio-common/tst-printf-format-skeleton.c
@@ -41,6 +41,10 @@ 
    MINEXP	[optional] Minimum exponent integer constant.  Set to the
 		*_MIN_EXP value for the argument type handled, so that
 		subnormal values can be told apart from normal ones.
+   UNSUPPORTED_CONVS
+		[optional] String of conversions the verification cannot
+		model for the argument type handled.  Asking for one of
+		these produces no records and an unsupported status.
 
    Typedefs:
    type_t	Variadic function argument type.  Define to the promoted
@@ -67,6 +71,7 @@ 
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <support/test-driver.h>
 
 /* Set to nonzero to select all possible tuples with repetitions of 1..n
    elements from the set of flags as defined in FLAGS array below; n is
@@ -87,6 +92,11 @@ 
 #ifndef MINEXP
 # define MINEXP 0
 #endif
+/* Set to the conversions that cannot be verified for the type handled;
+   empty where they all can be, which is the usual case.  */
+#ifndef UNSUPPORTED_CONVS
+# define UNSUPPORTED_CONVS ""
+#endif
 
 /* The list of conversions permitted for the '#' flag, the '0' flag,
    and precision respectively.  */
@@ -333,6 +343,10 @@  do_test (int argc, char *argv[])
       return EXIT_FAILURE;
     }
 
+  c = *argv[1];
+  if (strchr (UNSUPPORTED_CONVS, c) != NULL)
+    return EXIT_UNSUPPORTED;
+
   mtrace ();
 
   if (PREC != 0 && printf ("prec:%i\n", PREC) < 0)
@@ -347,7 +361,6 @@  do_test (int argc, char *argv[])
       return EXIT_FAILURE;
     }
 
-  c = *argv[1];
   for (v = 0; v < array_length (vals); v++)
     {
       if (printf ("val:%" REF_FMT "\n", REF_VAL (vals[v])) < 0)