From 41137b82fa1d3f6987d042d505202906bfd2c846 Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Sat, 22 Oct 2016 13:02:55 -0600 Subject: [PATCH] Add strtold() --- include/stdlib.h | 14 +-- libc/stdlib/Make.defs | 2 +- libc/stdlib/lib_strtod.c | 8 +- libc/stdlib/lib_strtof.c | 4 - libc/stdlib/lib_strtold.c | 248 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 259 insertions(+), 17 deletions(-) create mode 100644 libc/stdlib/lib_strtold.c diff --git a/include/stdlib.h b/include/stdlib.h index 09eb2cf6c0d..d10628ac413 100644 --- a/include/stdlib.h +++ b/include/stdlib.h @@ -80,13 +80,6 @@ # define environ get_environ_ptr() #endif -/* The function strtold() is not currently implemented in NuttX. Ideally, a - * custom implementation should be provided because, of course, there is a - * loss of 32-bits of accuracy in this conversion. - */ - -#define strtold(a,b) (long double)strtod(a,b) - /**************************************************************************** * Public Type Definitions ****************************************************************************/ @@ -197,8 +190,13 @@ long long strtoll(FAR const char *nptr, FAR char **endptr, int base); unsigned long long strtoull(FAR const char *nptr, FAR char **endptr, int base); #endif -double_t strtod(FAR const char *str, FAR char **endptr); float strtof(FAR const char *str, FAR char **endptr); +#ifdef CONFIG_HAVE_DOUBLE +double strtod(FAR const char *str, FAR char **endptr); +#endif +#ifdef CONFIG_HAVE_LONG_DOUBLE +long double strtold(FAR const char *str, FAR char **endptr); +#endif #define atoi(nptr) ((int)strtol((nptr), NULL, 10)) #define atol(nptr) strtol((nptr), NULL, 10) diff --git a/libc/stdlib/Make.defs b/libc/stdlib/Make.defs index d08a0bb4343..ec3977575d0 100644 --- a/libc/stdlib/Make.defs +++ b/libc/stdlib/Make.defs @@ -39,7 +39,7 @@ CSRCS += lib_abs.c lib_abort.c lib_div.c lib_ldiv.c lib_lldiv.c CSRCS += lib_imaxabs.c lib_itoa.c lib_labs.c lib_llabs.c CSRCS += lib_bsearch.c lib_rand.c lib_qsort.c lib_srand.c CSRCS += lib_strtol.c lib_strtoll.c lib_strtoul.c lib_strtoull.c -CSRCS += lib_strtod.c lib_strtof.c lib_checkbase.c +CSRCS += lib_strtod.c lib_strtof.c lib_strtold.c lib_checkbase.c ifeq ($(CONFIG_FS_WRITABLE),y) CSRCS += lib_mktemp.c lib_mkstemp.c diff --git a/libc/stdlib/lib_strtod.c b/libc/stdlib/lib_strtod.c index 7d8d08b5329..16d0dd331a8 100644 --- a/libc/stdlib/lib_strtod.c +++ b/libc/stdlib/lib_strtod.c @@ -71,7 +71,7 @@ static inline int is_real(double x) { - const double_t infinite = 1.0/0.0; + const double infinite = 1.0/0.0; return (x < infinite) && (x >= -infinite); } @@ -87,9 +87,9 @@ static inline int is_real(double x) * ****************************************************************************/ -double_t strtod(FAR const char *str, FAR char **endptr) +double strtod(FAR const char *str, FAR char **endptr) { - double_t number; + double number; int exponent; int negative; FAR char *p = (FAR char *) str; @@ -97,7 +97,7 @@ double_t strtod(FAR const char *str, FAR char **endptr) int n; int num_digits; int num_decimals; - const double_t infinite = 1.0/0.0; + const double infinite = 1.0/0.0; /* Skip leading whitespace */ diff --git a/libc/stdlib/lib_strtof.c b/libc/stdlib/lib_strtof.c index f081db0ad46..6c19d6d2857 100644 --- a/libc/stdlib/lib_strtof.c +++ b/libc/stdlib/lib_strtof.c @@ -48,8 +48,6 @@ #include #include -#ifdef CONFIG_HAVE_DOUBLE - /**************************************************************************** * Pre-processor definitions ****************************************************************************/ @@ -248,5 +246,3 @@ errout: return number; } - -#endif /* CONFIG_HAVE_DOUBLE */ diff --git a/libc/stdlib/lib_strtold.c b/libc/stdlib/lib_strtold.c new file mode 100644 index 00000000000..b0d10399f9d --- /dev/null +++ b/libc/stdlib/lib_strtold.c @@ -0,0 +1,248 @@ +/**************************************************************************** + * libc/stdlib/lib_strtod.c + * Convert string to long double + * + * Copyright (C) 2002 Michael Ringgaard. All rights reserved. + * Copyright (C) 2006-2007 H. Peter Anvin. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include +#include +#include + +#ifdef CONFIG_HAVE_LONG_DOUBLE + +/**************************************************************************** + * Pre-processor definitions + ****************************************************************************/ + +/* These are predefined with GCC, but could be issues for other compilers. If + * not defined, an arbitrary big number is put in for now. These should be + * added to nuttx/compiler for your compiler. + */ + +#if !defined(__LDBL_MIN_EXP__) || !defined(__LDBL_MAX_EXP__) +# ifdef CONFIG_CPP_HAVE_WARNING +# warning "Size of exponent is unknown" +# endif +# undef __LDBL_MIN_EXP__ +# define __LDBL_MIN_EXP__ (-1021) +# undef __LDBL_MAX_EXP__ +# define __LDBL_MAX_EXP__ (1024) +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static inline int is_real(long double x) +{ + const long double infinite = 1.0L/0.0L; + return (x < infinite) && (x >= -infinite); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/***************************************************(************************ + * Name: strtold + * + * Description: + * Convert a string to a long double value + * + ****************************************************************************/ + +long double strtold(FAR const char *str, FAR char **endptr) +{ + long double number; + int exponent; + int negative; + FAR char *p = (FAR char *) str; + long double p10; + int n; + int num_digits; + int num_decimals; + const long double infinite = 1.0L/0.0L; + + /* Skip leading whitespace */ + + while (isspace(*p)) + { + p++; + } + + /* Handle optional sign */ + + negative = 0; + switch (*p) + { + case '-': + negative = 1; /* Fall through to increment position */ + case '+': + p++; + default: + break; + } + + number = 0.0L; + exponent = 0; + num_digits = 0; + num_decimals = 0; + + /* Process string of digits */ + + while (isdigit(*p)) + { + number = number * 10.0L + (long double)(*p - '0'); + p++; + num_digits++; + } + + /* Process decimal part */ + + if (*p == '.') + { + p++; + + while (isdigit(*p)) + { + number = number * 10.0L + (long double)(*p - '0'); + p++; + num_digits++; + num_decimals++; + } + + exponent -= num_decimals; + } + + if (num_digits == 0) + { + set_errno(ERANGE); + number = 0.0L; + goto errout; + } + + /* Correct for sign */ + + if (negative) + { + number = -number; + } + + /* Process an exponent string */ + + if (*p == 'e' || *p == 'E') + { + /* Handle optional sign */ + + negative = 0; + switch (*++p) + { + case '-': + negative = 1; /* Fall through to increment pos */ + case '+': + p++; + default: + break; + } + + /* Process string of digits */ + + n = 0; + while (isdigit(*p)) + { + n = n * 10 + (*p - '0'); + p++; + } + + if (negative) + { + exponent -= n; + } + else + { + exponent += n; + } + } + + if (exponent < __LDBL_MIN_EXP__ || + exponent > __LDBL_MAX_EXP__) + { + set_errno(ERANGE); + number = infinite; + goto errout; + } + + /* Scale the result */ + + p10 = 10.; + n = exponent; + if (n < 0) n = -n; + while (n) + { + if (n & 1) + { + if (exponent < 0) + { + number /= p10; + } + else + { + number *= p10; + } + } + + n >>= 1; + p10 *= p10; + } + + if (!is_real(number)) + { + set_errno(ERANGE); + } + +errout: + if (endptr) + { + *endptr = p; + } + + return number; +} + +#endif /* CONFIG_HAVE_LONG_DOUBLE */