summaryrefslogtreecommitdiff
path: root/src/stdlib
diff options
context:
space:
mode:
authorRich Felker <dalias@aerifal.cx>2012-04-10 11:52:55 -0400
committerRich Felker <dalias@aerifal.cx>2012-04-10 11:52:55 -0400
commit415c4cd7fdb3e8b7476fbb2be2390f4592cf5165 (patch)
tree8dd9bb181dd259227dd6101633f81880cd0157f3 /src/stdlib
parent3be616c1df7ee176b5e00b9f493136ca7385ec46 (diff)
downloadmusl-415c4cd7fdb3e8b7476fbb2be2390f4592cf5165.tar.gz
new floating point parser/converter
this version is intended to be fully conformant to the ISO C, POSIX, and IEEE standards for conversion of decimal/hex floating point strings to float, double, and long double (ld64 or ld80 only at present) values. in particular, all results are intended to be rounded correctly according to the current rounding mode. further, this implementation aims to set the floating point underflow, overflow, and inexact flags to reflect the conversion performed. a moderate amount of testing has been performed (by nsz and myself) prior to integration of the code in musl, but it still may have bugs. so far, only strto(d|ld|f) use the new code. scanf integration will be done as a separate commit, and i will add implementations of the wide character functions later.
Diffstat (limited to 'src/stdlib')
-rw-r--r--src/stdlib/strtod.c11
-rw-r--r--src/stdlib/strtof.c11
-rw-r--r--src/stdlib/strtold.c103
3 files changed, 31 insertions, 94 deletions
diff --git a/src/stdlib/strtod.c b/src/stdlib/strtod.c
index 388058fe..98b992a1 100644
--- a/src/stdlib/strtod.c
+++ b/src/stdlib/strtod.c
@@ -1,6 +1,15 @@
#include <stdlib.h>
+#include "floatscan.h"
+#include "stdio_impl.h"
double strtod(const char *s, char **p)
{
- return strtold(s, p);
+ FILE f = {
+ .buf = (void *)s, .rpos = (void *)s,
+ .rend = (void *)-1, .lock = -1
+ };
+ off_t cnt;
+ double y = __floatscan(&f, -1, 1, 1, &cnt);
+ if (p) *p = (char *)s + cnt;
+ return y;
}
diff --git a/src/stdlib/strtof.c b/src/stdlib/strtof.c
index 07b32df4..2dc349a9 100644
--- a/src/stdlib/strtof.c
+++ b/src/stdlib/strtof.c
@@ -1,6 +1,15 @@
#include <stdlib.h>
+#include "floatscan.h"
+#include "stdio_impl.h"
float strtof(const char *s, char **p)
{
- return strtold(s, p);
+ FILE f = {
+ .buf = (void *)s, .rpos = (void *)s,
+ .rend = (void *)-1, .lock = -1
+ };
+ off_t cnt;
+ float y = __floatscan(&f, -1, 0, 1, &cnt);
+ if (p) *p = (char *)s + cnt;
+ return y;
}
diff --git a/src/stdlib/strtold.c b/src/stdlib/strtold.c
index ec464c15..40ecc122 100644
--- a/src/stdlib/strtold.c
+++ b/src/stdlib/strtold.c
@@ -1,96 +1,15 @@
#include <stdlib.h>
-#include <errno.h>
-#include <ctype.h>
+#include "floatscan.h"
+#include "stdio_impl.h"
-static int valid_exp(const unsigned char *s)
+long double strtold(const char *s, char **p)
{
- return isdigit(*s) || ((s[0]=='+'||s[0]=='-') && isdigit(s[1]));
-}
-
-long double strtold(const char *s1, char **p)
-{
- const unsigned char *s = (void *)s1;
- long double x = 0;
- long double frac;
- int sign = 0;
- int nonzero = 0;
- int radix = '.';
- long e;
- int saved_errno = errno;
-
- if (!p) p = (char **)&s1;
-
- /* Initial whitespace */
- for (; isspace(*s); s++);
-
- /* Optional sign */
- if (*s == '-') sign = *s++;
- else if (*s == '+') s++;
-
- /* Handle infinities and NaNs. */
- if ((s[0]|32)=='i' && (s[1]|32)=='n' && (s[2]|32)=='f') {
- *p = (char *)s + 3;
- return sign ? -1.0/0.0 : 1.0/0.0;
- } else if ((s[0]|32)=='n' && (s[1]|32)=='a' && (s[2]|32)=='n') {
- *p = (char *)s + 3;
- return 0.0/0.0;
- }
-
- /* Possible hex float */
- if (s[0]=='0' && (s[1]|32)=='x') {
- /* Mantissa must be non-degenerate */
- if (!isxdigit(s[2]) && (s[2]!=radix || !isxdigit(s[3]))) {
- /* Decimal float 0, 'x' extraneous */
- *p = (char *)++s;
- return 0;
- }
- /* We have a real hex float */
- s += 2;
- for (; isxdigit(*s); s++) {
- x = 16*x + (isdigit(*s)?*s-'0':(*s|32)-'a'+10);
- if (*s!='0') nonzero=1;
- }
- if (*s == radix) {
- frac = 1.0/16.0;
- for (s++; isxdigit(*s); s++) {
- x += frac * (isdigit(*s)?*s-'0':(*s|32)-'a'+10);
- frac *= 1.0/16.0;
- if (*s!='0') nonzero=1;
- }
- }
- if ((*s|32) == 'p' && valid_exp(s+1)) {
- e = strtol((void *)(s+1), (void *)&s, 10);
- for (; e>0; e--) x *= 2.0;
- for (; e<0; e++) x *= 0.5;
- }
- goto finish;
- }
-
- /* Mantissa must be non-degenerate */
- if (!isdigit(s[0]) && (s[0]!=radix || !isdigit(s[1]))) {
- *p = (char *)s1;
- return 0;
- }
-
- for (; isdigit(*s); s++) {
- x = 10*x + *s-'0';
- if (*s!='0') nonzero=1;
- }
- if (*s == radix) {
- frac = 10.0;
- for (s++; isdigit(*s); s++) {
- x += (*s-'0') / frac;
- frac *= 10.0;
- if (*s!='0') nonzero=1;
- }
- }
- if ((*s|32)=='e' && valid_exp(s+1)) {
- e = strtol((void *)++s, (void *)&s, 10);
- for (; e>0; e--) x *= 10.0;
- for (; e<0; e++) x /= 10.0;
- }
-finish:
- errno = ((nonzero && !x) || !(1.0/x)) ? ERANGE : saved_errno;
- *p = (char*)s;
- return sign ? -x : x;
+ FILE f = {
+ .buf = (void *)s, .rpos = (void *)s,
+ .rend = (void *)-1, .lock = -1
+ };
+ off_t cnt;
+ long double y = __floatscan(&f, -1, 2, 1, &cnt);
+ if (p) *p = (char *)s + cnt;
+ return y;
}