[RFC PATCH 1/1] newlib: strtold: Import strtorQ for 128-bit long double support

Zakaria Fadli <[email protected]>
Newsgroups gmane.comp.lib.newlib
Message-ID <[email protected]>
The implementation of strtorQ is imported from FreeBSD's gdtoa library (By David
M. Gay) with some adaptations to fit with newlib.

`strtorQ.c` enables `strtold` to perform correct parsing on targets where long
double uses the IEEE754 binary128 format (113-bit mantissa), such as AArch64.
Without this patch, strtold would wrongly fallback to `strtorx` which will
parse into a 80-bit long double and give invalid result.

* libc/stdlib/strtorQ.c: New file, adapted from FreeBSD gdtoa.
* libc/stdlib/strtold.c (_strtold_impl): New helper selecting strtorx
  for 80-bit and strtorQ for 128-bit long double.
  (_strtold_r, strtold_l, strtold): Use _strtold_impl.
* libc/stdlib/mprec.h (_strtorQ_l): Declare.
* libc/stdlib/Makefile.inc (libc_a_SOURCES): Add strtorQ.c.
* newlib/Makefile.in: Regenerate with automake

Signed-off-by: Zakaria Fadli <[email protected]>
---
 newlib/Makefile.in              |  19 +++++
 newlib/libc/stdlib/Makefile.inc |   1 +
 newlib/libc/stdlib/mprec.h      |   2 +
 newlib/libc/stdlib/strtold.c    |  56 +++++++-------
 newlib/libc/stdlib/strtorQ.c    | 129 ++++++++++++++++++++++++++++++++
 5 files changed, 178 insertions(+), 29 deletions(-)
 create mode 100644 newlib/libc/stdlib/strtorQ.c

diff --git a/newlib/Makefile.in b/newlib/Makefile.in
index 9a32646ab..c1515407b 100644
--- a/newlib/Makefile.in
+++ b/newlib/Makefile.in
@@ -125,6 +125,7 @@ check_PROGRAMS =
 @HAVE_LONG_DOUBLE_TRUE@am__append_6 = \
 @HAVE_LONG_DOUBLE_TRUE@	libc/stdlib/strtodg.c \
 @HAVE_LONG_DOUBLE_TRUE@	libc/stdlib/strtold.c \
+@HAVE_LONG_DOUBLE_TRUE@	libc/stdlib/strtorQ.c \
 @HAVE_LONG_DOUBLE_TRUE@	libc/stdlib/strtorx.c \
 @HAVE_LONG_DOUBLE_TRUE@	libc/stdlib/wcstold.c
 
@@ -1045,6 +1046,7 @@ am__dirstamp = $(am__leading_dot)dirstamp
 @HAVE_LONG_DOUBLE_TRUE@am__objects_2 =  \
 @HAVE_LONG_DOUBLE_TRUE@	libc/stdlib/libc_a-strtodg.$(OBJEXT) \
 @HAVE_LONG_DOUBLE_TRUE@	libc/stdlib/libc_a-strtold.$(OBJEXT) \
+@HAVE_LONG_DOUBLE_TRUE@	libc/stdlib/libc_a-strtorQ.$(OBJEXT) \
 @HAVE_LONG_DOUBLE_TRUE@	libc/stdlib/libc_a-strtorx.$(OBJEXT) \
 @HAVE_LONG_DOUBLE_TRUE@	libc/stdlib/libc_a-wcstold.$(OBJEXT)
 am__objects_3 = libc/stdlib/libc_a-a64l.$(OBJEXT) \
@@ -5611,6 +5613,8 @@ libc/stdlib/libc_a-strtodg.$(OBJEXT): libc/stdlib/$(am__dirstamp) \
 	libc/stdlib/$(DEPDIR)/$(am__dirstamp)
 libc/stdlib/libc_a-strtold.$(OBJEXT): libc/stdlib/$(am__dirstamp) \
 	libc/stdlib/$(DEPDIR)/$(am__dirstamp)
+libc/stdlib/libc_a-strtorQ.$(OBJEXT): libc/stdlib/$(am__dirstamp) \
+	libc/stdlib/$(DEPDIR)/$(am__dirstamp)
 libc/stdlib/libc_a-strtorx.$(OBJEXT): libc/stdlib/$(am__dirstamp) \
 	libc/stdlib/$(DEPDIR)/$(am__dirstamp)
 libc/stdlib/libc_a-wcstold.$(OBJEXT): libc/stdlib/$(am__dirstamp) \
@@ -13652,6 +13656,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@libc/stdlib/$(DEPDIR)/libc_a-strtold.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@libc/stdlib/$(DEPDIR)/libc_a-strtoll.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@libc/stdlib/$(DEPDIR)/libc_a-strtoll_r.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libc/stdlib/$(DEPDIR)/libc_a-strtorQ.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@libc/stdlib/$(DEPDIR)/libc_a-strtorx.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@libc/stdlib/$(DEPDIR)/libc_a-strtoul.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@libc/stdlib/$(DEPDIR)/libc_a-strtoull.Po@am__quote@
@@ -21559,6 +21564,20 @@ libc/stdlib/libc_a-strtold.obj: libc/stdlib/strtold.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libc_a_CPPFLAGS) $(CPPFLAGS) $(libc_a_CFLAGS) $(CFLAGS) -c -o libc/stdlib/libc_a-strtold.obj `if test -f 'libc/stdlib/strtold.c'; then $(CYGPATH_W) 'libc/stdlib/strtold.c'; else $(CYGPATH_W) '$(srcdir)/libc/stdlib/strtold.c'; fi`
 
+libc/stdlib/libc_a-strtorQ.o: libc/stdlib/strtorQ.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libc_a_CPPFLAGS) $(CPPFLAGS) $(libc_a_CFLAGS) $(CFLAGS) -MT libc/stdlib/libc_a-strtorQ.o -MD -MP -MF libc/stdlib/$(DEPDIR)/libc_a-strtorQ.Tpo -c -o libc/stdlib/libc_a-strtorQ.o `test -f 'libc/stdlib/strtorQ.c' || echo '$(srcdir)/'`libc/stdlib/strtorQ.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) libc/stdlib/$(DEPDIR)/libc_a-strtorQ.Tpo libc/stdlib/$(DEPDIR)/libc_a-strtorQ.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='libc/stdlib/strtorQ.c' object='libc/stdlib/libc_a-strtorQ.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libc_a_CPPFLAGS) $(CPPFLAGS) $(libc_a_CFLAGS) $(CFLAGS) -c -o libc/stdlib/libc_a-strtorQ.o `test -f 'libc/stdlib/strtorQ.c' || echo '$(srcdir)/'`libc/stdlib/strtorQ.c
+
+libc/stdlib/libc_a-strtorQ.obj: libc/stdlib/strtorQ.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libc_a_CPPFLAGS) $(CPPFLAGS) $(libc_a_CFLAGS) $(CFLAGS) -MT libc/stdlib/libc_a-strtorQ.obj -MD -MP -MF libc/stdlib/$(DEPDIR)/libc_a-strtorQ.Tpo -c -o libc/stdlib/libc_a-strtorQ.obj `if test -f 'libc/stdlib/strtorQ.c'; then $(CYGPATH_W) 'libc/stdlib/strtorQ.c'; else $(CYGPATH_W) '$(srcdir)/libc/stdlib/strtorQ.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) libc/stdlib/$(DEPDIR)/libc_a-strtorQ.Tpo libc/stdlib/$(DEPDIR)/libc_a-strtorQ.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='libc/stdlib/strtorQ.c' object='libc/stdlib/libc_a-strtorQ.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libc_a_CPPFLAGS) $(CPPFLAGS) $(libc_a_CFLAGS) $(CFLAGS) -c -o libc/stdlib/libc_a-strtorQ.obj `if test -f 'libc/stdlib/strtorQ.c'; then $(CYGPATH_W) 'libc/stdlib/strtorQ.c'; else $(CYGPATH_W) '$(srcdir)/libc/stdlib/strtorQ.c'; fi`
+
 libc/stdlib/libc_a-strtorx.o: libc/stdlib/strtorx.c
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libc_a_CPPFLAGS) $(CPPFLAGS) $(libc_a_CFLAGS) $(CFLAGS) -MT libc/stdlib/libc_a-strtorx.o -MD -MP -MF libc/stdlib/$(DEPDIR)/libc_a-strtorx.Tpo -c -o libc/stdlib/libc_a-strtorx.o `test -f 'libc/stdlib/strtorx.c' || echo '$(srcdir)/'`libc/stdlib/strtorx.c
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) libc/stdlib/$(DEPDIR)/libc_a-strtorx.Tpo libc/stdlib/$(DEPDIR)/libc_a-strtorx.Po
diff --git a/newlib/libc/stdlib/Makefile.inc b/newlib/libc/stdlib/Makefile.inc
index 9812add76..6510cdfdd 100644
--- a/newlib/libc/stdlib/Makefile.inc
+++ b/newlib/libc/stdlib/Makefile.inc
@@ -83,6 +83,7 @@ if HAVE_LONG_DOUBLE
 libc_a_SOURCES += \
 	%D%/strtodg.c \
 	%D%/strtold.c \
+	%D%/strtorQ.c \
 	%D%/strtorx.c \
 	%D%/wcstold.c
 endif # HAVE_LONG_DOUBLE
diff --git a/newlib/libc/stdlib/mprec.h b/newlib/libc/stdlib/mprec.h
index 83de932c2..c2d0a0ac3 100644
--- a/newlib/libc/stdlib/mprec.h
+++ b/newlib/libc/stdlib/mprec.h
@@ -385,6 +385,8 @@ double		_strtod_l (struct _reent *ptr, const char *__restrict s00,
 #if defined (_HAVE_LONG_DOUBLE) && !defined (_LDBL_EQ_DBL)
 int		_strtorx_l (struct _reent *, const char *, char **, int,
 			    void *, locale_t);
+int		_strtorQ_l (struct _reent *, const char *, char **, int,
+			    void *, locale_t);
 int		_strtodg_l (struct _reent *p, const char *s00, char **se,
 			    struct FPI *fpi, Long *exp, __ULong *bits,
 			    locale_t);
diff --git a/newlib/libc/stdlib/strtold.c b/newlib/libc/stdlib/strtold.c
index 6bd1c2cbb..57cc38e30 100644
--- a/newlib/libc/stdlib/strtold.c
+++ b/newlib/libc/stdlib/strtold.c
@@ -58,47 +58,45 @@ __flt_rounds(void)
 #define FLT_ROUNDS 0
 #endif
 
-long double
-_strtold_r (struct _reent *ptr, const char *__restrict s00,
-	    char **__restrict se)
-{
-#ifdef _LDBL_EQ_DBL
-  /* On platforms where long double is as wide as double.  */
-  return _strtod_l (ptr, s00, se, __get_current_locale ());
-#else
+/*
+ * The core implementation for the strtold family of functions, handling
+ * different long double formats.
+ */
+static long double
+_strtold_impl(struct _reent *ptr, const char *__restrict s00,
+              char **__restrict se, locale_t loc) {
+#if defined(_LDBL_EQ_DBL)
+  /* On platforms where long double is as wide as double. */
+  return _strtod_l(ptr, s00, se, loc);
+#elif LDBL_MANT_DIG == 64 /* For 80 bit long doubles */
   long double result;
-
-  _strtorx_l (ptr, s00, se, FLT_ROUNDS, &result, __get_current_locale ());
+  _strtorx_l(ptr, s00, se, FLT_ROUNDS, &result, loc);
   return result;
+#elif LDBL_MANT_DIG == 113 /* For 128bit long doubles */
+  long double result;
+  _strtorQ_l(ptr, s00, se, FLT_ROUNDS, &result, loc);
+  return result;
+#else
+#warning "strtold not implemented for this long double format"
+  return (long double)_strtod_l(ptr, s00, se, loc);
 #endif
 }
 
 long double
-strtold_l (const char *__restrict s00, char **__restrict se, locale_t loc)
-{
-#ifdef _LDBL_EQ_DBL
-  /* On platforms where long double is as wide as double.  */
-  return _strtod_l (_REENT, s00, se, loc);
-#else
-  long double result;
+_strtold_r(struct _reent *ptr, const char *__restrict s00,
+           char **__restrict se) {
+  return _strtold_impl(ptr, s00, se, __get_current_locale());
+}
 
-  _strtorx_l (_REENT, s00, se, FLT_ROUNDS, &result, loc);
-  return result;
-#endif
+long double
+strtold_l(const char *__restrict s00, char **__restrict se, locale_t loc) {
+  return _strtold_impl(_REENT, s00, se, loc);
 }
 
 long double
 strtold (const char *__restrict s00, char **__restrict se)
 {
-#ifdef _LDBL_EQ_DBL
-  /* On platforms where long double is as wide as double.  */
-  return _strtod_l (_REENT, s00, se, __get_current_locale ());
-#else
-  long double result;
-
-  _strtorx_l (_REENT, s00, se, FLT_ROUNDS, &result, __get_current_locale ());
-  return result;
-#endif
+  return _strtold_impl(_REENT, s00, se, __get_current_locale());
 }
 
 #endif /* _HAVE_LONG_DOUBLE */
diff --git a/newlib/libc/stdlib/strtorQ.c b/newlib/libc/stdlib/strtorQ.c
new file mode 100644
index 000000000..b3205e3b7
--- /dev/null
+++ b/newlib/libc/stdlib/strtorQ.c
@@ -0,0 +1,129 @@
+/****************************************************************
+
+The author of this software is David M. Gay.
+
+Copyright (C) 1998, 2000 by Lucent Technologies
+All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the name of Lucent or any of its entities
+not be used in advertising or publicity pertaining to
+distribution of the software without specific, written prior
+permission.
+
+LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
+SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
+IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+
+****************************************************************/
+
+/* Please send bug reports to David M. Gay (dmg at acm dot org,
+ * with " at " changed at "@" and " dot " changed to ".").	*/
+
+#include <_ansi.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include "mprec.h"
+#include "gdtoa.h"
+
+#if defined (_HAVE_LONG_DOUBLE) && !defined (_LDBL_EQ_DBL)
+
+/* one or the other of IEEE_MC68k or IEEE_8087 should be #defined */
+
+#ifdef IEEE_MC68k
+#define _0 0
+#define _1 1
+#define _2 2
+#define _3 3
+#endif
+#ifdef IEEE_8087
+#define _0 3
+#define _1 2
+#define _2 1
+#define _3 0
+#endif
+
+ void
+#ifdef KR_headers
+ULtoQ(L, bits, exp, k) ULong *L; ULong *bits; Long exp; int k;
+#else
+ULtoQ(ULong *L, ULong *bits, Long exp, int k)
+#endif
+{
+	switch(k & STRTOG_Retmask) {
+	  case STRTOG_NoNumber:
+	  case STRTOG_Zero:
+		L[0] = L[1] = L[2] = L[3] = 0;
+		break;
+
+	  case STRTOG_Normal:
+		L[_3] = bits[0];
+		L[_2] = bits[1];
+		L[_1] = bits[2];
+		L[_0] = (bits[3] & ~0x10000) | ((exp + 0x3fff + 112) << 16);
+		break;
+
+	  case STRTOG_NaNbits:
+		L[_3] = bits[0];
+		L[_2] = bits[1];
+		L[_1] = bits[2];
+		L[_0] = (bits[3] & ~0x10000)
+		    | (((exp + 0x3fff + 112) << 16) | (1 << 15));
+		break;
+
+	  case STRTOG_Denormal:
+		L[_3] = bits[0];
+		L[_2] = bits[1];
+		L[_1] = bits[2];
+		L[_0] = bits[3];
+		break;
+
+	  case STRTOG_Infinite:
+		L[_0] = 0x7fff0000;
+		L[_1] = L[_2] = L[_3] = 0;
+		break;
+
+	  case STRTOG_NaN:
+		*((long double*)L) = __builtin_nanl ("");
+	  }
+	if (k & STRTOG_Neg)
+		L[_0] |= 0x80000000L;
+	}
+
+ int
+#ifdef KR_headers
+_strtorQ_l(s, sp, rounding, L, locale) struct _reent *p; CONST char *s;
+char **sp; int rounding; void *L; locale_t locale;
+#else
+_strtorQ_l(struct _reent *p, CONST char *s, char **sp,
+           int rounding, void *L, locale_t locale)
+#endif
+{
+	static FPI fpi0 = { 113, 1-16383-113+1, 32766-16383-113+1, 1, SI };
+	FPI *fpi, fpi1;
+	ULong bits[4];
+	Long exp;
+	int k;
+
+	fpi = &fpi0;
+	if (rounding != FPI_Round_near) {
+		fpi1 = fpi0;
+		fpi1.rounding = rounding;
+		fpi = &fpi1;
+		}
+	k = _strtodg_l(p, s, sp, fpi, &exp, bits, locale);
+	ULtoQ((ULong*)L, bits, exp, k);
+	return k;
+	}
+
+#endif /* _HAVE_LONG_DOUBLE && !_LDBL_EQ_DBL */
-- 
2.43.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.