v04i013: Replacement for strtod()
ok at quintus.UUCP
ok at quintus.UUCP
Wed Aug 3 11:19:11 AEST 1988
Posting-number: Volume 4, Issue 13
Submitted-by: "A. Nonymous" <ok at quintus.UUCP>
Archive-name: strtod
strtod() is a nice function, but it's often missing or broken.
Here is a replacement for it which does the parsing and checking
required of strtod() but calls atof() to do the conversion.
---- cut here ---- cut here ---- cut here ---- cut here ---- cut here ----
#!/bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #!/bin/sh line.
# 2. Save the resulting test in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
# README
# makefile
# str2dbl.c
sed -e 's/^X//' >README <<'------ EOF ------'
XFILES:
X README - this file
X makefile - a trivial makefile
X str2dbl.c - defines str2dbl(), requires atof() + float support
X
Xdouble str2dbl(char *str, char **ptr)
X converts the character string str points to to a double-precision
X floating-point number and returns it. str2dbl() recognises
X
X <space>... [+|-] <digit>... [. <digit>...] [<exponent>]
X
X where <exponent> is
X
X e|E [+|-| ] <digit> <digit>...
X
X If ptr is not (char**)NULL, *ptr is assigned a pointer to the
X character just after the last character accepted by str2dbl().
X {This will typically be a layout character or NUL.}
X
X If there aren't any digits at the front (e.g. the input is
X "e45" or "Fred" or "-gotcha-") str2dbl() will make *ptr point
X to the whole of str (even if there were leading spaces and signs)
X and will return 0.0.
X
X If there is some problem with the exponent (e.g. the input is
X "1.2e%45") str2dbl() will reject all of the exponent, making *ptr
X point to the 'e', and will convert only the preceding characters.
X
X A single space after the 'e' or 'E' of an exponent is accepted as
X if it were a '+' sign, because some output formatting routines
X generate numbers that look like that. Spaces are not otherwise
X accepted inside numbers.
X
X If the correct value cannot be represented, str2dbl() sets errno
X to ERANGE, and returns +/-HUGE for overflow, +/-ZERO for underflow.
X
XWARNING:
X The source code as provided is set up for 64-bit IEEE-754 floating
X point. VAX and IBM floating-point formats are different, so the
X numeric range is different. Some machines which look as though
X they have IEEE floats may not support infinities or denormalised
X numbers, in which case the numeric range is different. You will
X have to determine the correct values for your machine.
X
------ EOF ------
ls -l README
sed -e 's/^X//' >makefile <<'------ EOF ------'
Xstr2dbl.o: str2dbl.c
------ EOF ------
ls -l makefile
sed -e 's/^X//' >str2dbl.c <<'------ EOF ------'
X/* File : str2dbl.c
X Author : Richard A. O'Keefe @ Quintus Computer Systems, Inc.
X Updated: Tuesday August 2nd, 1988
X Defines: double str2dbl(char *str, char**ptr)
X*/
X
X/* This is an implementation of the strtod() function described in the
X System V manuals, with a different name to avoid linker problems.
X All that str2dbl() does itself is check that the argument is well-formed
X and is in range. It leaves the work of conversion to atof(), which is
X assumed to exist and deliver correct results (if they can be represented).
X
X There are two reasons why this should be provided to the net:
X (a) some UNIX systems do not yet have strtod(), or do not have it
X available in the BSD "universe" (but they do have atof()).
X (b) some of the UNIX systems that *do* have it get it wrong.
X (some crash with large arguments, some assign the wrong *ptr value).
X There is a reason why *we* are providing it: we need a correct version
X of strtod(), and if we give this one away maybe someone will look for
X mistakes in it and fix them for us (:-).
X*/
X
X/* The following constants are machine-specific. MD{MIN,MAX}EXPT are
X integers and MD{MIN,MAX}FRAC are strings such that
X 0.${MDMAXFRAC}e${MDMAXEXPT} is the largest representable double,
X 0.${MDMINFRAC}e${MDMINEXPT} is the smallest representable +ve double
X MD{MIN,MAX}FRAC must not have any trailing zeros.
X The values here are for IEEE-754 64-bit floats.
X It is not perfectly clear to me whether an IEEE infinity should be
X returned for overflow, nor what a portable way of writing one is,
X so HUGE is just 0.MAXFRAC*10**MAXEXPT (this seems still to be the
X UNIX convention).
X
X I do know about <values.h>, but the whole point of this file is that
X we can't always trust that stuff to be there or to be correct.
X*/
Xstatic int MDMINEXPT = {-323};
Xstatic char MDMINFRAC[] = "494065645841246544";
Xstatic double ZERO = 0.0;
X
Xstatic int MDMAXEXPT = { 309};
Xstatic char MDMAXFRAC[] = "17976931348623147";
Xstatic double HUGE = 1.7976931348623147e308;
X
Xextern double atof(); /* Only called when result known to be ok */
X
X#include <errno.h>
Xextern int errno;
X
Xdouble str2dbl(str, ptr)
X char *str;
X char **ptr;
X {
X int sign, scale, dotseen;
X int esign, expt;
X char *save;
X register char *sp, *dp;
X register int c;
X char *buforg, *buflim;
X char buffer[64]; /* 45-digit significand + */
X /* 13-digit exponent */
X sp = str;
X while (*sp == ' ') sp++;
X sign = 1;
X if (*sp == '-') sign -= 2, sp++;
X dotseen = 0, scale = 0;
X dp = buffer;
X *dp++ = '0'; *dp++ = '.';
X buforg = dp, buflim = buffer+48;
X for (save = sp; c = *sp; sp++)
X if (c == '.') {
X if (dotseen) break;
X dotseen++;
X } else
X if ((unsigned)(c-'0') > (unsigned)('9'-'0')) {
X break;
X } else
X if (c == '0') {
X if (dp != buforg) {
X /* This is not the first digit, so we want to keep it */
X if (dp < buflim) *dp++ = c;
X } else {
X /* No non-zero digits seen yet */
X /* If a . has been seen, scale must be adjusted */
X if (dotseen) scale--;
X }
X } else {
X /* This is a nonzero digit, so we want to keep it */
X if (dp < buflim) *dp++ = c;
X /* If it precedes a ., scale must be adjusted */
X if (!dotseen) scale++;
X }
X if (sp == save) {
X if (ptr) *ptr = str;
X errno = EDOM; /* what should this be? */
X return ZERO;
X }
X
X while (dp > buforg && dp[-1] == '0') --dp;
X if (dp == buforg) *dp++ = '0';
X *dp = '\0';
X /* Now the contents of buffer are
X +--+--------+-+--------+
X |0.|fraction|\|leftover|
X +--+--------+-+--------+
X ^dp points here
X where fraction begins with 0 iff it is "0", and has at most
X 45 digits in it, and leftover is at least 16 characters.
X */
X save = sp, expt = 0, esign = 1;
X do {
X c = *sp++;
X if (c != 'e' && c != 'E') break;
X c = *sp++;
X if (c == '-') esign -= 2, c = *sp++; else
X if (c == '+' || c == ' ') c = *sp++;
X if ((unsigned)(c-'0') > (unsigned)('9'-'0')) break;
X while (c == '0') c = *sp++;
X for (; (unsigned)(c-'0') <= (unsigned)('9'-'0'); c = *sp++)
X expt = expt*10 + c-'0';
X if (esign < 0) expt = -expt;
X save = sp-1;
X } while (0);
X if (ptr) *ptr = save;
X expt += scale;
X /* Now the number is sign*0.fraction*10**expt */
X errno = ERANGE;
X if (expt > MDMAXEXPT) {
X return HUGE*sign;
X } else
X if (expt == MDMAXEXPT) {
X if (strcmp(buforg, MDMAXFRAC) > 0) return HUGE*sign;
X } else
X if (expt < MDMINEXPT) {
X return ZERO*sign;
X } else
X if (expt == MDMINEXPT) {
X if (strcmp(buforg, MDMINFRAC) < 0) return ZERO*sign;
X }
X /* We have now established that the number can be */
X /* represented without overflow or underflow */
X (void) sprintf(dp, "E%d", expt);
X errno = 0;
X return atof(buffer)*sign;
X }
X
------ EOF ------
ls -l str2dbl.c
exit 0
More information about the Comp.sources.misc
mailing list