fix various bugs in strtold:
[musl] / src / stdlib / strtold.c
1 #include <stdlib.h>
2 #include <errno.h>
3 #include <ctype.h>
4
5 static int valid_exp(const unsigned char *s)
6 {
7         return isdigit(*s) || ((s[0]=='+'||s[0]=='-') && isdigit(s[1]));
8 }
9
10 long double strtold(const char *s1, char **p)
11 {
12         const unsigned char *s = (void *)s1;
13         long double x = 0;
14         long double frac;
15         int sign = 0;
16         int nonzero = 0;
17         int radix = '.';
18         long e;
19         int saved_errno = errno;
20
21         if (!p) p = (char **)&s1;
22
23         /* Initial whitespace */
24         for (; isspace(*s); s++);
25
26         /* Optional sign */
27         if (*s == '-') sign = *s++;
28         else if (*s == '+') s++;
29
30         /* Handle infinities and NaNs. */
31         if ((s[0]|32)=='i' && (s[1]|32)=='n' && (s[2]|32)=='f') {
32                 *p = (char *)s + 3;
33                 return sign ? -1.0/0.0 : 1.0/0.0;
34         } else if ((s[0]|32)=='n' && (s[1]|32)=='a' && (s[2]|32)=='n') {
35                 *p = (char *)s + 3;
36                 return 0.0/0.0;
37         }
38
39         /* Possible hex float */
40         if (s[0]=='0' && (s[1]|32)=='x') {
41                 /* Mantissa must be non-degenerate */
42                 if (!isxdigit(s[2]) && (s[2]!=radix || !isxdigit(s[3]))) {
43                         /* Decimal float 0, 'x' extraneous */
44                         *p = (char *)++s;
45                         return 0;
46                 }
47                 /* We have a real hex float */
48                 s += 2;
49                 for (; isxdigit(*s); s++) {
50                         x = 16*x + (isdigit(*s)?*s-'0':(*s|32)-'a'+10);
51                         if (*s!='0') nonzero=1;
52                 }
53                 if (*s == radix) {
54                         frac = 1.0/16.0;
55                         for (s++; isxdigit(*s); s++) {
56                                 x += frac * (isdigit(*s)?*s-'0':(*s|32)-'a'+10);
57                                 frac *= 1.0/16.0;
58                                 if (*s!='0') nonzero=1;
59                         }
60                 }
61                 if ((*s|32) == 'p' && valid_exp(s+1)) {
62                         e = strtol((void *)(s+1), (void *)&s, 10);
63                         for (; e>0; e--) x *= 2.0;
64                         for (; e<0; e++) x *= 0.5;
65                 }
66                 goto finish;
67         }
68
69         /* Mantissa must be non-degenerate */
70         if (!isdigit(s[0]) && (s[0]!=radix || !isdigit(s[1]))) {
71                 *p = (char *)s1;
72                 return 0;
73         }
74
75         for (; isdigit(*s); s++) {
76                 x = 10*x + *s-'0';
77                 if (*s!='0') nonzero=1;
78         }
79         if (*s == radix) {
80                 frac = 10.0;
81                 for (s++; isdigit(*s); s++) {
82                         x += (*s-'0') / frac;
83                         frac *= 10.0;
84                         if (*s!='0') nonzero=1;
85                 }
86         }
87         if ((*s|32)=='e' && valid_exp(s+1)) {
88                 e = strtol((void *)++s, (void *)&s, 10);
89                 for (; e>0; e--) x *= 10.0;
90                 for (; e<0; e++) x /= 10.0;
91         }
92 finish:
93         errno = ((nonzero && !x) || !(1.0/x)) ? ERANGE : saved_errno;
94         *p = (char*)s;
95         return sign ? -x : x;
96 }