|
1 | | -/* This is not a proper strtod() implementation, but sufficient for Python. |
2 | | - Python won't detect floating point constant overflow, though. */ |
| 1 | +/* comp.sources.misc strtod(), as posted in comp.lang.tcl, |
| 2 | + with bugfix for "123000.0" and acceptance of space after 'e' sign nuked. |
3 | 3 |
|
4 | | -extern int errno; |
| 4 | + ************************************************************ |
| 5 | + * YOU MUST EDIT THE MACHINE-DEPENDENT DEFINITIONS BELOW!!! * |
| 6 | + ************************************************************ |
| 7 | +*/ |
5 | 8 |
|
6 | | -extern int strlen(); |
7 | | -extern double atof(); |
| 9 | +/* File : stdtod.c (Modified version of str2dbl.c) |
| 10 | + Author : Richard A. O'Keefe @ Quintus Computer Systems, Inc. |
| 11 | + Updated: Tuesday August 2nd, 1988 |
| 12 | + Defines: double strtod (char *str, char**ptr) |
| 13 | +*/ |
8 | 14 |
|
9 | | -double |
10 | | -strtod(p, pp) |
11 | | - char *p; |
12 | | - char **pp; |
13 | | -{ |
14 | | - double res; |
| 15 | +/* This is an implementation of the strtod() function described in the |
| 16 | + System V manuals, with a different name to avoid linker problems. |
| 17 | + All that str2dbl() does itself is check that the argument is well-formed |
| 18 | + and is in range. It leaves the work of conversion to atof(), which is |
| 19 | + assumed to exist and deliver correct results (if they can be represented). |
15 | 20 |
|
16 | | - if (pp) |
17 | | - *pp = p + strlen(p); |
18 | | - res = atof(p); |
19 | | - errno = 0; |
20 | | - return res; |
| 21 | + There are two reasons why this should be provided to the net: |
| 22 | + (a) some UNIX systems do not yet have strtod(), or do not have it |
| 23 | + available in the BSD "universe" (but they do have atof()). |
| 24 | + (b) some of the UNIX systems that *do* have it get it wrong. |
| 25 | + (some crash with large arguments, some assign the wrong *ptr value). |
| 26 | + There is a reason why *we* are providing it: we need a correct version |
| 27 | + of strtod(), and if we give this one away maybe someone will look for |
| 28 | + mistakes in it and fix them for us (:-). |
| 29 | +*/ |
| 30 | + |
| 31 | +/* The following constants are machine-specific. MD{MIN,MAX}EXPT are |
| 32 | + integers and MD{MIN,MAX}FRAC are strings such that |
| 33 | + 0.${MDMAXFRAC}e${MDMAXEXPT} is the largest representable double, |
| 34 | + 0.${MDMINFRAC}e${MDMINEXPT} is the smallest representable +ve double |
| 35 | + MD{MIN,MAX}FRAC must not have any trailing zeros. |
| 36 | + The values here are for IEEE-754 64-bit floats. |
| 37 | + It is not perfectly clear to me whether an IEEE infinity should be |
| 38 | + returned for overflow, nor what a portable way of writing one is, |
| 39 | + so HUGE is just 0.MAXFRAC*10**MAXEXPT (this seems still to be the |
| 40 | + UNIX convention). |
| 41 | +
|
| 42 | + I do know about <values.h>, but the whole point of this file is that |
| 43 | + we can't always trust that stuff to be there or to be correct. |
| 44 | +*/ |
| 45 | +static int MDMINEXPT = {-323}; |
| 46 | +static char MDMINFRAC[] = "494065645841246544"; |
| 47 | +static double ZERO = 0.0; |
| 48 | + |
| 49 | +static int MDMAXEXPT = { 309}; |
| 50 | +static char MDMAXFRAC[] = "17976931348623147"; |
| 51 | +static double HUGE = 1.7976931348623147e308; |
| 52 | + |
| 53 | +extern double atof(); /* Only called when result known to be ok */ |
| 54 | + |
| 55 | +#include <errno.h> |
| 56 | +extern int errno; |
| 57 | + |
| 58 | +double strtod(str, ptr) |
| 59 | + char *str; |
| 60 | + char **ptr; |
| 61 | + { |
| 62 | + int sign, scale, dotseen; |
| 63 | + int esign, expt; |
| 64 | + char *save; |
| 65 | + register char *sp, *dp; |
| 66 | + register int c; |
| 67 | + char *buforg, *buflim; |
| 68 | + char buffer[64]; /* 45-digit significand + */ |
| 69 | + /* 13-digit exponent */ |
| 70 | + sp = str; |
| 71 | + while (*sp == ' ') sp++; |
| 72 | + sign = 1; |
| 73 | + if (*sp == '-') sign -= 2, sp++; |
| 74 | + dotseen = 0, scale = 0; |
| 75 | + dp = buffer; |
| 76 | + *dp++ = '0'; *dp++ = '.'; |
| 77 | + buforg = dp, buflim = buffer+48; |
| 78 | + for (save = sp; c = *sp; sp++) |
| 79 | + if (c == '.') { |
| 80 | + if (dotseen) break; |
| 81 | + dotseen++; |
| 82 | + } else |
| 83 | + if ((unsigned)(c-'0') > (unsigned)('9'-'0')) { |
| 84 | + break; |
| 85 | + } else |
| 86 | + if (c == '0') { |
| 87 | + if (dp != buforg) { |
| 88 | + /* This is not the first digit, so we want to keep it */ |
| 89 | + if (dp < buflim) *dp++ = c; |
| 90 | + if (!dotseen) scale++; |
| 91 | + } else { |
| 92 | + /* No non-zero digits seen yet */ |
| 93 | + /* If a . has been seen, scale must be adjusted */ |
| 94 | + if (dotseen) scale--; |
| 95 | + } |
| 96 | + } else { |
| 97 | + /* This is a nonzero digit, so we want to keep it */ |
| 98 | + if (dp < buflim) *dp++ = c; |
| 99 | + /* If it precedes a ., scale must be adjusted */ |
| 100 | + if (!dotseen) scale++; |
| 101 | + } |
| 102 | + if (sp == save) { |
| 103 | + if (ptr) *ptr = str; |
| 104 | + errno = EDOM; /* what should this be? */ |
| 105 | + return ZERO; |
| 106 | + } |
21 | 107 |
|
22 | | -} |
| 108 | + while (dp > buforg && dp[-1] == '0') --dp; |
| 109 | + if (dp == buforg) *dp++ = '0'; |
| 110 | + *dp = '\0'; |
| 111 | + /* Now the contents of buffer are |
| 112 | + +--+--------+-+--------+ |
| 113 | + |0.|fraction|\|leftover| |
| 114 | + +--+--------+-+--------+ |
| 115 | + ^dp points here |
| 116 | + where fraction begins with 0 iff it is "0", and has at most |
| 117 | + 45 digits in it, and leftover is at least 16 characters. |
| 118 | + */ |
| 119 | + save = sp, expt = 0, esign = 1; |
| 120 | + do { |
| 121 | + c = *sp++; |
| 122 | + if (c != 'e' && c != 'E') break; |
| 123 | + c = *sp++; |
| 124 | + if (c == '-') esign -= 2, c = *sp++; else |
| 125 | + if (c == '+' /* || c == ' ' */ ) c = *sp++; |
| 126 | + if ((unsigned)(c-'0') > (unsigned)('9'-'0')) break; |
| 127 | + while (c == '0') c = *sp++; |
| 128 | + for (; (unsigned)(c-'0') <= (unsigned)('9'-'0'); c = *sp++) |
| 129 | + expt = expt*10 + c-'0'; |
| 130 | + if (esign < 0) expt = -expt; |
| 131 | + save = sp-1; |
| 132 | + } while (0); |
| 133 | + if (ptr) *ptr = save; |
| 134 | + expt += scale; |
| 135 | + /* Now the number is sign*0.fraction*10**expt */ |
| 136 | + errno = ERANGE; |
| 137 | + if (expt > MDMAXEXPT) { |
| 138 | + return HUGE*sign; |
| 139 | + } else |
| 140 | + if (expt == MDMAXEXPT) { |
| 141 | + if (strcmp(buforg, MDMAXFRAC) > 0) return HUGE*sign; |
| 142 | + } else |
| 143 | + if (expt < MDMINEXPT) { |
| 144 | + return ZERO*sign; |
| 145 | + } else |
| 146 | + if (expt == MDMINEXPT) { |
| 147 | + if (strcmp(buforg, MDMINFRAC) < 0) return ZERO*sign; |
| 148 | + } |
| 149 | + /* We have now established that the number can be */ |
| 150 | + /* represented without overflow or underflow */ |
| 151 | + (void) sprintf(dp, "E%d", expt); |
| 152 | + errno = 0; |
| 153 | + return atof(buffer)*sign; |
| 154 | + } |
0 commit comments