diff options
author | Zakaria Fadli <fadli@adacore.com> | 2025-09-22 19:11:33 +0200 |
---|---|---|
committer | Jeff Johnston <jjohnstn@redhat.com> | 2025-09-22 14:54:00 -0400 |
commit | 63dc988ac7454b701ee923590f18df4103587994 (patch) | |
tree | cef15e41c706117180943e674a7ce8ec056b069b | |
parent | 8a5d39527f9a56d1a623e86d30af6b590fd1472d (diff) | |
download | newlib-main.zip newlib-main.tar.gz newlib-main.tar.bz2 |
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 <fadli@adacore.com>
-rw-r--r-- | newlib/Makefile.in | 19 | ||||
-rw-r--r-- | newlib/libc/stdlib/Makefile.inc | 1 | ||||
-rw-r--r-- | newlib/libc/stdlib/mprec.h | 2 | ||||
-rw-r--r-- | newlib/libc/stdlib/strtold.c | 56 | ||||
-rw-r--r-- | newlib/libc/stdlib/strtorQ.c | 129 |
5 files changed, 178 insertions, 29 deletions
diff --git a/newlib/Makefile.in b/newlib/Makefile.in index c5492c6..50154d6 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 @@ -1090,6 +1091,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) \ @@ -5656,6 +5658,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) \ @@ -13769,6 +13773,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@ @@ -21806,6 +21811,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 9812add..6510cdf 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 83de932..c2d0a0a 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 6bd1c2c..57cc38e 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 0000000..e9e0b9f --- /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(p, 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 */ |