diff mbox

[3/6] float128: Add strfromf128

Message ID 1495803396-14558-4-git-send-email-gftg@linux.vnet.ibm.com
State New
Headers show

Commit Message

Gabriel F. T. Gomes May 26, 2017, 12:56 p.m. UTC
Add strfromf128 to stdlib when _Float128 support is enabled.

2016-11-07  Gabriel F. T. Gomes  <gftg@linux.vnet.ibm.com>

	* NEWS: Mention the addition of strfromf128.
	* include/gmp.h: Include bits/floatn.h for _Float128 support.
	* stdio-common/printf-parsemb.c (__parse_one_specmb): Initialize
	spec->info.is_binary128 to zero.
	* stdio-common/printf.h (printf_info): Add new member is_binary128
	to indicate that the number being converted to string is compatible
	with the IEC 60559 binary128 format.
	* stdio-common/printf_fp.c (__printf_fp_l): Add code to deal with
	_Float128 numbers.
	* stdio-common/printf_fphex.c (__printf_fphex): Likewise.
	* stdio-common/printf_size.c (__printf_size): Likewise.
	* stdio-common/vfprintf.c (process_arg): Initialize member
	info.is_binary128 to zero.
	* stdlib/fpioconst.h (FLT128_MAX_10_EXP_LOG): New macro.
	* stdlib/stdlib.h: Include bits/floatn.h for _Float128 support.
	(strfromf128): New declaration.
	* stdlib/strfrom-skeleton.c (STRFROM): Set member info.is_binary128
	to one.
	* sysdeps/ieee754/float128/Makefile: Add strfromf128.
	* sysdeps/ieee754/float128/Versions: Likewise.
	* sysdeps/ieee754/float128/strfromf128.c: New file.
---
 NEWS                                   |   3 +
 include/gmp.h                          |   2 +
 stdio-common/printf-parsemb.c          |   1 +
 stdio-common/printf.h                  |   4 +-
 stdio-common/printf_fp.c               | 103 ++++++++++++++++++++++++-
 stdio-common/printf_fphex.c            | 134 +++++++++++++++++++++++++++++++++
 stdio-common/printf_size.c             |  29 +++++++
 stdio-common/vfprintf.c                |  10 ++-
 stdlib/fpioconst.h                     |   4 +
 stdlib/stdlib.h                        |   8 ++
 stdlib/strfrom-skeleton.c              |   6 ++
 sysdeps/ieee754/float128/Makefile      |   2 +-
 sysdeps/ieee754/float128/Versions      |   5 ++
 sysdeps/ieee754/float128/strfromf128.c |  25 ++++++
 14 files changed, 328 insertions(+), 8 deletions(-)
 create mode 100644 sysdeps/ieee754/float128/strfromf128.c

Comments

Joseph Myers May 26, 2017, 3:56 p.m. UTC | #1
On Fri, 26 May 2017, Gabriel F. T. Gomes wrote:

> diff --git a/NEWS b/NEWS
> index b4ecd62..8cb17cc 100644
> --- a/NEWS
> +++ b/NEWS
> @@ -66,6 +66,9 @@ Version 2.26
>  * The port to Native Client running on ARMv7-A (--host=arm-nacl) has been
>    removed.
>  
> +* The function strfromf128, from ISO/IEC TS 18661-3:2015, is added to libc.
> +  It converts a _Float128 value into string.

Such a NEWS entry, for a function not available for all glibc 
configurations, needs to indicate what systems the function is available 
on.  (I'd say that means there should be one NEWS entry for all the 
float128 functions, added only when the powerpc64le support is enabled, 
rather than piecemeal entries before then.)

> diff --git a/include/gmp.h b/include/gmp.h
> index 95d6c16..e6f635e 100644
> --- a/include/gmp.h
> +++ b/include/gmp.h
> @@ -6,6 +6,8 @@
>  
>  #include <stdlib/gmp.h>
>  
> +#include <bits/floatn.h>
> +
>  /* Now define the internal interfaces.  */
>  extern mp_size_t __mpn_extract_double (mp_ptr res_ptr, mp_size_t size,
>  				       int *expt, int *is_neg,

Presumably this belongs in patch 2, which adds a test 
of__HAVE_DISTINCT_FLOAT128 to this header; it seems to have nothing to do 
with this patch.

> @@ -328,6 +339,52 @@ __printf_fp_l (FILE *fp, locale_t loc,
>      grouping = NULL;
>  
>    /* Fetch the argument value.	*/
> +#if __HAVE_DISTINCT_FLOAT128
> +  if (info->is_binary128)

I don't like this duplication of a large section of code.

As I see it, the code inside the conditional only varies between types 
for: the field name (f128 in this case); the type name; the 
__mpn_extract_* function called; the MANT_DIG value.  (It *used* to differ 
more, before we moved to using type-generic isnan / signbit / isinf macros 
throughout glibc instead of direct calls to the functions those macros 
might call.)

So refactor the existing code to have a macro for the code inside the 
conditional, used for both the long double and double cases.  Then you 
just need to add a third call to the macro for the _Float128 case.

>    /* Fetch the argument value.	*/
> +#if __HAVE_DISTINCT_FLOAT128
> +  if (info->is_binary128)

The same comment about duplication applies here.

> +#if __HAVE_DISTINCT_FLOAT128
> +  /* This block is copied from sysdeps/ieee754/ldbl-128/printf_fphex.c.  */
> +  if (info->is_binary128)

And here, the definition of PRINT_FPHEX_LONG_DOUBLE should be refactored 
in some way to allow it to be shared.  (It's fine for type names such as 
union ieee854_float128 to be visible internally in glibc for both 
binary128 long double and for _Float128, if it helps have a common macro 
that can be used as-is in defining both PRINT_FPHEX_LONG_DOUBLE and e.g. 
PRINT_FPHEX_FLOAT128.)

> diff --git a/stdlib/strfrom-skeleton.c b/stdlib/strfrom-skeleton.c
> index 811a29c..5841919 100644
> --- a/stdlib/strfrom-skeleton.c
> +++ b/stdlib/strfrom-skeleton.c
> @@ -132,6 +132,12 @@ STRFROM (char *dest, size_t size, const char *format, FLOAT f)
>       which type of floating-point number is being passed.  */
>    info.is_long_double = __builtin_types_compatible_p (FLOAT, long double);
>  
> +  /* Similarly, the function strfromf128 passes a floating-point number in
> +     _Float128 format to printf_fp.  */
> +#if __HAVE_DISTINCT_FLOAT128
> +  info.is_binary128 = __builtin_types_compatible_p (FLOAT, _Float128);
> +#endif

In other places you set is_binary128 even when !__HAVE_DISTINCT_FLOAT128.  
It's not *used* when !__HAVE_DISTINCT_FLOAT128 - is there a deliberate 
choice that in this place it shouldn't be set either?
diff mbox

Patch

diff --git a/NEWS b/NEWS
index b4ecd62..8cb17cc 100644
--- a/NEWS
+++ b/NEWS
@@ -66,6 +66,9 @@  Version 2.26
 * The port to Native Client running on ARMv7-A (--host=arm-nacl) has been
   removed.
 
+* The function strfromf128, from ISO/IEC TS 18661-3:2015, is added to libc.
+  It converts a _Float128 value into string.
+
 Security related changes:
 
 * The DNS stub resolver limits the advertised UDP buffer size to 1200 bytes,
diff --git a/include/gmp.h b/include/gmp.h
index 95d6c16..e6f635e 100644
--- a/include/gmp.h
+++ b/include/gmp.h
@@ -6,6 +6,8 @@ 
 
 #include <stdlib/gmp.h>
 
+#include <bits/floatn.h>
+
 /* Now define the internal interfaces.  */
 extern mp_size_t __mpn_extract_double (mp_ptr res_ptr, mp_size_t size,
 				       int *expt, int *is_neg,
diff --git a/stdio-common/printf-parsemb.c b/stdio-common/printf-parsemb.c
index edb066e..a42336c 100644
--- a/stdio-common/printf-parsemb.c
+++ b/stdio-common/printf-parsemb.c
@@ -79,6 +79,7 @@  __parse_one_specmb (const UCHAR_T *format, size_t posn,
   spec->info.extra = 0;
   spec->info.pad = ' ';
   spec->info.wide = sizeof (UCHAR_T) > 1;
+  spec->info.is_binary128 = 0;
 
   /* Test for positional argument.  */
   if (ISDIGIT (*format))
diff --git a/stdio-common/printf.h b/stdio-common/printf.h
index 5d82e8d..6b207d2 100644
--- a/stdio-common/printf.h
+++ b/stdio-common/printf.h
@@ -47,7 +47,9 @@  struct printf_info
   unsigned int is_char:1;	/* hh flag.  */
   unsigned int wide:1;		/* Nonzero for wide character streams.  */
   unsigned int i18n:1;		/* I flag.  */
-  unsigned int __pad:4;		/* Unused so far.  */
+  unsigned int is_binary128:1;	/* Floating-point argument is ABI-compatible
+				   with IEC 60559 binary128.  */
+  unsigned int __pad:3;		/* Unused so far.  */
   unsigned short int user;	/* Bits for user-installed modifiers.  */
   wchar_t pad;			/* Padding character.  */
 };
diff --git a/stdio-common/printf_fp.c b/stdio-common/printf_fp.c
index 7845d96..f6335c5 100644
--- a/stdio-common/printf_fp.c
+++ b/stdio-common/printf_fp.c
@@ -218,6 +218,9 @@  __printf_fp_l (FILE *fp, locale_t loc,
     {
       double dbl;
       __long_double_t ldbl;
+#if __HAVE_DISTINCT_FLOAT128
+      _Float128 f128;
+#endif
     }
   fpnum;
 
@@ -234,9 +237,17 @@  __printf_fp_l (FILE *fp, locale_t loc,
   const char *special = NULL;
   const wchar_t *wspecial = NULL;
 
+  /* When _Float128 is enabled in the library and ABI-distinct from long
+     double, we need mp_limbs enough for any of them.  */
+#if __HAVE_DISTINCT_FLOAT128
+# define GREATER_MANT_DIG FLT128_MANT_DIG
+#else
+# define GREATER_MANT_DIG LDBL_MANT_DIG
+#endif
   /* We need just a few limbs for the input before shifting to the right
      position.	*/
-  mp_limb_t fp_input[(LDBL_MANT_DIG + BITS_PER_MP_LIMB - 1) / BITS_PER_MP_LIMB];
+  mp_limb_t fp_input[(GREATER_MANT_DIG + BITS_PER_MP_LIMB - 1)
+		     / BITS_PER_MP_LIMB];
   /* We need to shift the contents of fp_input by this amount of bits.	*/
   int to_shift = 0;
 
@@ -328,6 +339,52 @@  __printf_fp_l (FILE *fp, locale_t loc,
     grouping = NULL;
 
   /* Fetch the argument value.	*/
+#if __HAVE_DISTINCT_FLOAT128
+  if (info->is_binary128)
+    {
+      fpnum.f128 = *(const _Float128 *) args[0];
+
+      /* Check for special values: not a number or infinity.  */
+      if (isnan (fpnum.f128))
+	{
+	  is_neg = signbit (fpnum.f128);
+	  if (isupper (info->spec))
+	    {
+	      special = "NAN";
+	      wspecial = L"NAN";
+	    }
+	    else
+	      {
+		special = "nan";
+		wspecial = L"nan";
+	      }
+	}
+      else if (isinf (fpnum.f128))
+	{
+	  is_neg = signbit (fpnum.f128);
+	  if (isupper (info->spec))
+	    {
+	      special = "INF";
+	      wspecial = L"INF";
+	    }
+	  else
+	    {
+	      special = "inf";
+	      wspecial = L"inf";
+	    }
+	}
+      else
+	{
+	  p.fracsize = __mpn_extract_float128 (fp_input,
+					       (sizeof (fp_input) /
+						sizeof (fp_input[0])),
+					       &p.exponent, &is_neg,
+					       fpnum.f128);
+	  to_shift = 1 + p.fracsize * BITS_PER_MP_LIMB - FLT128_MANT_DIG;
+	}
+    }
+  else
+#endif /* __HAVE_DISTINCT_FLOAT128 */
 #ifndef __NO_LONG_DOUBLE_MATH
   if (info->is_long_double && sizeof (long double) > sizeof (double))
     {
@@ -450,7 +507,8 @@  __printf_fp_l (FILE *fp, locale_t loc,
   {
     mp_size_t bignum_size = ((abs (p.exponent) + BITS_PER_MP_LIMB - 1)
 			     / BITS_PER_MP_LIMB
-			     + (LDBL_MANT_DIG / BITS_PER_MP_LIMB > 2 ? 8 : 4))
+			     + (GREATER_MANT_DIG / BITS_PER_MP_LIMB > 2
+				? 8 : 4))
 			    * sizeof (mp_limb_t);
     p.frac = (mp_limb_t *) alloca (bignum_size);
     p.tmp = (mp_limb_t *) alloca (bignum_size);
@@ -465,7 +523,15 @@  __printf_fp_l (FILE *fp, locale_t loc,
     {
       /* |FP| >= 8.0.  */
       int scaleexpo = 0;
-      int explog = LDBL_MAX_10_EXP_LOG;
+      int explog;
+#if __HAVE_DISTINCT_FLOAT128
+      if (info->is_binary128)
+	explog = FLT128_MAX_10_EXP_LOG;
+      else
+	explog = LDBL_MAX_10_EXP_LOG;
+#else
+      explog = LDBL_MAX_10_EXP_LOG;
+#endif
       int exp10 = 0;
       const struct mp_power *powers = &_fpioconst_pow10[explog + 1];
       int cnt_h, cnt_l, i;
@@ -499,6 +565,27 @@  __printf_fp_l (FILE *fp, locale_t loc,
 	    {
 	      if (p.scalesize == 0)
 		{
+#if __HAVE_DISTINCT_FLOAT128
+		  if ((FLT128_MANT_DIG
+			    > _FPIO_CONST_OFFSET * BITS_PER_MP_LIMB)
+			   && info->is_binary128)
+		    {
+#define _FLT128_FPIO_CONST_SHIFT \
+  (((FLT128_MANT_DIG + BITS_PER_MP_LIMB - 1) / BITS_PER_MP_LIMB) \
+   - _FPIO_CONST_OFFSET)
+		      /* 64bit const offset is not enough for
+			 IEEE 854 quad long double (_Float128).  */
+		      p.tmpsize = powers->arraysize + _FLT128_FPIO_CONST_SHIFT;
+		      memcpy (p.tmp + _FLT128_FPIO_CONST_SHIFT,
+			      &__tens[powers->arrayoff],
+			      p.tmpsize * sizeof (mp_limb_t));
+		      MPN_ZERO (p.tmp, _FLT128_FPIO_CONST_SHIFT);
+		      /* Adjust p.exponent, as scaleexpo will be this much
+			 bigger too.  */
+		      p.exponent += _FLT128_FPIO_CONST_SHIFT * BITS_PER_MP_LIMB;
+		    }
+		  else
+#endif /* __HAVE_DISTINCT_FLOAT128 */
 #ifndef __NO_LONG_DOUBLE_MATH
 		  if (LDBL_MANT_DIG > _FPIO_CONST_OFFSET * BITS_PER_MP_LIMB
 		      && info->is_long_double)
@@ -639,7 +726,15 @@  __printf_fp_l (FILE *fp, locale_t loc,
     {
       /* |FP| < 1.0.  */
       int exp10 = 0;
-      int explog = LDBL_MAX_10_EXP_LOG;
+      int explog;
+#if __HAVE_DISTINCT_FLOAT128
+      if (info->is_binary128)
+	explog = FLT128_MAX_10_EXP_LOG;
+      else
+	explog = LDBL_MAX_10_EXP_LOG;
+#else
+      explog = LDBL_MAX_10_EXP_LOG;
+#endif
       const struct mp_power *powers = &_fpioconst_pow10[explog + 1];
 
       /* Now shift the input value to its right place.	*/
diff --git a/stdio-common/printf_fphex.c b/stdio-common/printf_fphex.c
index b207e00..d5573b1 100644
--- a/stdio-common/printf_fphex.c
+++ b/stdio-common/printf_fphex.c
@@ -31,6 +31,10 @@ 
 #include <stdbool.h>
 #include <rounding-mode.h>
 
+#if __HAVE_DISTINCT_FLOAT128
+# include "ieee754_float128.h"
+#endif
+
 /* #define NDEBUG 1*/		/* Undefine this for debugging assertions.  */
 #include <assert.h>
 
@@ -94,6 +98,9 @@  __printf_fphex (FILE *fp,
     {
       union ieee754_double dbl;
       long double ldbl;
+#if __HAVE_DISTINCT_FLOAT128
+      _Float128 flt128;
+#endif
     }
   fpnum;
 
@@ -159,6 +166,45 @@  __printf_fphex (FILE *fp,
 
 
   /* Fetch the argument value.	*/
+#if __HAVE_DISTINCT_FLOAT128
+  if (info->is_binary128)
+    {
+      fpnum.flt128 = *(const _Float128 *) args[0];
+
+      /* Check for special values: not a number or infinity.  */
+      if (isnan (fpnum.flt128))
+	{
+	  if (isupper (info->spec))
+	    {
+	      special = "NAN";
+	      wspecial = L"NAN";
+	    }
+	  else
+	    {
+	      special = "nan";
+	      wspecial = L"nan";
+	    }
+	}
+      else
+	{
+	  if (isinf (fpnum.flt128))
+	    {
+	      if (isupper (info->spec))
+		{
+		  special = "INF";
+		  wspecial = L"INF";
+		}
+	      else
+		{
+		  special = "inf";
+		  wspecial = L"inf";
+		}
+	    }
+	}
+      negative = signbit (fpnum.flt128);
+    }
+  else
+#endif  /* __HAVE_DISTINCT_FLOAT128 */
 #ifndef __NO_LONG_DOUBLE_MATH
   if (info->is_long_double && sizeof (long double) > sizeof (double))
     {
@@ -260,6 +306,94 @@  __printf_fphex (FILE *fp,
       return done;
     }
 
+#if __HAVE_DISTINCT_FLOAT128
+  /* This block is copied from sysdeps/ieee754/ldbl-128/printf_fphex.c.  */
+  if (info->is_binary128)
+    {
+      /* We have 112 bits of mantissa plus one implicit digit.  Since
+	 112 bits are representable without rest using hexadecimal
+	 digits we use only the implicit digits for the number before
+	 the decimal point.  */
+      unsigned long long int num0, num1;
+      union ieee854_float128 u;
+      u.d = fpnum.flt128;
+
+      num0 = (((unsigned long long int) u.ieee.mantissa0) << 32
+	     | u.ieee.mantissa1);
+      num1 = (((unsigned long long int) u.ieee.mantissa2) << 32
+	     | u.ieee.mantissa3);
+
+      zero_mantissa = (num0|num1) == 0;
+
+      if (sizeof (unsigned long int) > 6)
+	{
+	  numstr = _itoa_word (num1, numbuf + sizeof numbuf, 16,
+			       info->spec == 'A');
+	  wnumstr = _itowa_word (num1,
+				 wnumbuf + sizeof (wnumbuf) / sizeof (wchar_t),
+				 16, info->spec == 'A');
+	}
+      else
+	{
+	  numstr = _itoa (num1, numbuf + sizeof numbuf, 16,
+			  info->spec == 'A');
+	  wnumstr = _itowa (num1,
+			    wnumbuf + sizeof (wnumbuf) / sizeof (wchar_t),
+			    16, info->spec == 'A');
+	}
+
+      while (numstr > numbuf + (sizeof numbuf - 64 / 4))
+	{
+	  *--numstr = '0';
+	  *--wnumstr = L'0';
+	}
+
+      if (sizeof (unsigned long int) > 6)
+	{
+	  numstr = _itoa_word (num0, numstr, 16, info->spec == 'A');
+	  wnumstr = _itowa_word (num0, wnumstr, 16, info->spec == 'A');
+	}
+      else
+	{
+	  numstr = _itoa (num0, numstr, 16, info->spec == 'A');
+	  wnumstr = _itowa (num0, wnumstr, 16, info->spec == 'A');
+	}
+
+      /* Fill with zeroes.  */
+      while (numstr > numbuf + (sizeof numbuf - 112 / 4))
+	{
+	  *--numstr = '0';
+	  *--wnumstr = L'0';
+	}
+
+      leading = u.ieee.exponent == 0 ? '0' : '1';
+
+      exponent = u.ieee.exponent;
+
+      if (exponent == 0)
+	{
+	  if (zero_mantissa)
+	    expnegative = 0;
+	  else
+	    {
+	      /* This is a denormalized number.  */
+	      expnegative = 1;
+	      exponent = IEEE854_FLOAT128_BIAS - 1;
+	    }
+	}
+      else if (exponent >= IEEE854_FLOAT128_BIAS)
+	{
+	  expnegative = 0;
+	  exponent -= IEEE854_FLOAT128_BIAS;
+	}
+      else
+	{
+	  expnegative = 1;
+	  exponent = -(exponent - IEEE854_FLOAT128_BIAS);
+	}
+    }
+  else
+#endif /* __HAVE_DISTINCT_FLOAT128 */
   if (info->is_long_double == 0 || sizeof (double) == sizeof (long double))
     {
       /* We have 52 bits of mantissa plus one implicit digit.  Since
diff --git a/stdio-common/printf_size.c b/stdio-common/printf_size.c
index 9403aea..3449d17 100644
--- a/stdio-common/printf_size.c
+++ b/stdio-common/printf_size.c
@@ -104,6 +104,9 @@  __printf_size (FILE *fp, const struct printf_info *info,
     {
       union ieee754_double dbl;
       long double ldbl;
+#if __HAVE_DISTINCT_FLOAT128
+      _Float128 f128;
+#endif
     }
   fpnum;
   const void *ptr = &fpnum;
@@ -119,6 +122,32 @@  __printf_size (FILE *fp, const struct printf_info *info,
   int wide = info->wide;
 
   /* Fetch the argument value.	*/
+#if __HAVE_DISTINCT_FLOAT128
+  if (info->is_binary128)
+    {
+      fpnum.f128 = *(const _Float128 *) args[0];
+
+      /* Check for special values: not a number or infinity.  */
+      if (isnan (fpnum.f128))
+	{
+	  special = "nan";
+	  wspecial = L"nan";
+	}
+      else if (isinf (fpnum.f128))
+	{
+	  is_neg = signbit (fpnum.f128);
+	  special = "inf";
+	  wspecial = L"inf";
+	}
+      else
+	while (fpnum.f128 >= divisor && tag[1] != '\0')
+	  {
+	    fpnum.f128 /= divisor;
+	    ++tag;
+	  }
+    }
+  else
+#endif /* __HAVE_DISTINCT_FLOAT128 */
 #ifndef __NO_LONG_DOUBLE_MATH
   if (info->is_long_double && sizeof (long double) > sizeof (double))
     {
diff --git a/stdio-common/vfprintf.c b/stdio-common/vfprintf.c
index 2cf7c8a..b8c87a5 100644
--- a/stdio-common/vfprintf.c
+++ b/stdio-common/vfprintf.c
@@ -770,7 +770,8 @@  static const uint8_t jump_table[] =
 					.pad = pad,			      \
 					.extra = 0,			      \
 					.i18n = use_outdigits,		      \
-					.wide = sizeof (CHAR_T) != 1 };	      \
+					.wide = sizeof (CHAR_T) != 1,	      \
+					.is_binary128 = 0};		      \
 									      \
 	    if (is_long_double)						      \
 	      the_arg.pa_long_double = va_arg (ap, long double);	      \
@@ -788,6 +789,8 @@  static const uint8_t jump_table[] =
 		fspec->data_arg_type = PA_DOUBLE;			      \
 		fspec->info.is_long_double = 0;				      \
 	      }								      \
+	    /* Not supported by *printf functions.  */			      \
+	    fspec->info.is_binary128 = 0;				      \
 									      \
 	    function_done = __printf_fp (s, &fspec->info, &ptr);	      \
 	  }								      \
@@ -827,7 +830,8 @@  static const uint8_t jump_table[] =
 					.group = group,			      \
 					.pad = pad,			      \
 					.extra = 0,			      \
-					.wide = sizeof (CHAR_T) != 1 };	      \
+					.wide = sizeof (CHAR_T) != 1,	      \
+					.is_binary128 = 0};		      \
 									      \
 	    if (is_long_double)						      \
 	      the_arg.pa_long_double = va_arg (ap, long double);	      \
@@ -842,6 +846,8 @@  static const uint8_t jump_table[] =
 	    ptr = (const void *) &args_value[fspec->data_arg];		      \
 	    if (__ldbl_is_dbl)						      \
 	      fspec->info.is_long_double = 0;				      \
+	    /* Not supported by *printf functions.  */			      \
+	    fspec->info.is_binary128 = 0;				      \
 									      \
 	    function_done = __printf_fphex (s, &fspec->info, &ptr);	      \
 	  }								      \
diff --git a/stdlib/fpioconst.h b/stdlib/fpioconst.h
index 7e19b0d..aab46d6 100644
--- a/stdlib/fpioconst.h
+++ b/stdlib/fpioconst.h
@@ -44,6 +44,10 @@ 
    IBM extended precision).  */
 #include <bits/floatn.h>
 
+#if __HAVE_DISTINCT_FLOAT128
+# define FLT128_MAX_10_EXP_LOG	12 /* = floor(log_2(FLT128_MAX_10_EXP)) */
+#endif
+
 /* For strtold, we need powers of 10 up to floor (log_2 (LDBL_MANT_DIG
    - LDBL_MIN_EXP + 2)).  When _Float128 is enabled in libm and it is
    ABI-distinct from long double (e.g. on powerpc64le), we also need powers
diff --git a/stdlib/stdlib.h b/stdlib/stdlib.h
index 99125f2..1313aa5 100644
--- a/stdlib/stdlib.h
+++ b/stdlib/stdlib.h
@@ -51,6 +51,9 @@  __BEGIN_DECLS
 # endif
 #endif	/* X/Open or XPG7 and <sys/wait.h> not included.  */
 
+/* _FloatN API tests for enablement.  */
+#include <bits/floatn.h>
+
 /* Returned by `div'.  */
 typedef struct
   {
@@ -173,6 +176,11 @@  extern int strfromf (char *__dest, size_t __size, const char *__format,
 extern int strfroml (char *__dest, size_t __size, const char *__format,
 		     long double __f)
      __THROW __nonnull ((3));
+# if __HAVE_FLOAT128 && __GLIBC_USE (IEC_60559_TYPES_EXT)
+extern int strfromf128 (char *__dest, size_t __size, const char * __format,
+			_Float128 __f)
+     __THROW __nonnull ((3));
+# endif
 #endif
 
 
diff --git a/stdlib/strfrom-skeleton.c b/stdlib/strfrom-skeleton.c
index 811a29c..5841919 100644
--- a/stdlib/strfrom-skeleton.c
+++ b/stdlib/strfrom-skeleton.c
@@ -132,6 +132,12 @@  STRFROM (char *dest, size_t size, const char *format, FLOAT f)
      which type of floating-point number is being passed.  */
   info.is_long_double = __builtin_types_compatible_p (FLOAT, long double);
 
+  /* Similarly, the function strfromf128 passes a floating-point number in
+     _Float128 format to printf_fp.  */
+#if __HAVE_DISTINCT_FLOAT128
+  info.is_binary128 = __builtin_types_compatible_p (FLOAT, _Float128);
+#endif
+
   /* Set info according to the format string.  */
   info.prec = precision;
   info.spec = specifier;
diff --git a/sysdeps/ieee754/float128/Makefile b/sysdeps/ieee754/float128/Makefile
index 6a7b0e0..c07586c 100644
--- a/sysdeps/ieee754/float128/Makefile
+++ b/sysdeps/ieee754/float128/Makefile
@@ -1,3 +1,3 @@ 
 ifeq ($(subdir),stdlib)
-routines += float1282mpn
+routines += float1282mpn strfromf128
 endif
diff --git a/sysdeps/ieee754/float128/Versions b/sysdeps/ieee754/float128/Versions
index 9f431d9..caf2064 100644
--- a/sysdeps/ieee754/float128/Versions
+++ b/sysdeps/ieee754/float128/Versions
@@ -2,6 +2,11 @@ 
 %ifndef FLOAT128_VERSION
 % error "float128-abi.h must define FLOAT128_VERSION"
 %endif
+libc {
+  FLOAT128_VERSION {
+    strfromf128;
+  }
+}
 libm {
   FLOAT128_VERSION {
     __acosf128_finite;
diff --git a/sysdeps/ieee754/float128/strfromf128.c b/sysdeps/ieee754/float128/strfromf128.c
new file mode 100644
index 0000000..597c7e6
--- /dev/null
+++ b/sysdeps/ieee754/float128/strfromf128.c
@@ -0,0 +1,25 @@ 
+/* Definitions for strfromf128.  Implementation in stdlib/strfrom-skeleton.c.
+
+   Copyright (C) 2017 Free Software Foundation, Inc.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#define	FLOAT		_Float128
+#define STRFROM		strfromf128
+
+#include <bits/floatn.h>
+#include <float128_private.h>
+
+#include <stdlib/strfrom-skeleton.c>