nfs@notecnirp.Princeton.EDU (Norbert Schlenker) (01/10/90)
There are bugs in the posted atoi() and atol() routines. Most importantly, they consider characters less than '0' to be negative digits (e.g. '/' is treated as -1). They also don't handle '+' signs in front of numbers, which ANSI says they should. I enclose my routines, which don't have these problems. The multiplications in each routine have been replaced by shifts. On my 80386, this atoi() is slightly slower than a version which multiplies. On machines with relatively slower multipliers (e.g. 8088's and 68000's), the shifts should be a big win. Atol() is slightly faster with shifts, even on the 80386. ANSI also says that atoi() and atol() are somewhat special cases of strtol(), with error checking turned off. I enclose copies of ANSI compliant strtol() and strtoul() - they are much slower than atoi() and atol() [about a factor of 2 relative to atol()]. Norbert ----------------------------- Cut here -------------------------------- #!/bin/sh echo x - atoi.c sed '/^X/s///' > atoi.c << '/' X/* atoi.c ANSI 4.10.1.2 X * int atoi(const char *nptr); X * X * Converts a numeric string in base 10 to an integer. X */ X X#include <lib.h> X#include <ctype.h> X#include <stdlib.h> X X#ifdef atoi X#undef atoi X#endif X XPUBLIC int atoi(nptr) Xregister _CONST char *nptr; X{ X register int c; X int result = 0; X int negative = 0; X X while ((c = *nptr) && isspace(c)) /* skip leading white space */ X ++nptr; X X if (c == '+' || c == '-') { /* handle signs */ X negative = (c == '-'); X ++nptr; X } X X while ((c = *nptr++ - '0') >= 0 && c < 10) X result = (result << 1) + (result << 3) + c; X X return (negative) ? 0 - result : result; X} / echo x - atol.c sed '/^X/s///' > atol.c << '/' X/* atol.c ANSI 4.10.1.3 X * long int atol(const char *nptr); X * X * Converts a numeric string in base 10 to a long integer. X */ X X#include <lib.h> X#include <ctype.h> X#include <stdlib.h> X X#ifdef atol X#undef atol X#endif X XPUBLIC long int atol(nptr) Xregister _CONST char *nptr; X{ X register int c; X long int result = 0; X int negative = 0; X X while ((c = *nptr) && isspace(c)) /* skip leading white space */ X ++nptr; X X if (c == '+' || c == '-') { /* handle signs */ X negative = (c == '-'); X ++nptr; X } X X while ((c = *nptr++ - '0') >= 0 && c < 10) X result = (result << 1) + (result << 3) + c; X X return (negative) ? 0L - result : result; X} / echo x - strtol.c sed '/^X/s///' > strtol.c << '/' X/* strtol.c ANSI 4.10.1.5 X * long int strtol(const char *nptr, char **endptr, int base); X * X * Converts a numeric string, in various bases, to a long integer. X */ X X#include <lib.h> X#include <ctype.h> X#include <errno.h> X#include <limits.h> X#include <stdlib.h> X X#ifdef strtol X#undef strtol X#endif X XPUBLIC long int strtol(nptr, endptr, base) X_CONST char *nptr; Xchar **endptr; Xint base; X{ X register int c; X long int result = 0L; X long int limit; X int negative = 0; X int overflow = 0; X int saw_a_digit = 0; /* it's not a number without a digit */ X X if (endptr != (char **) NULL) /* set up default final pointer */ X *endptr = nptr; X X while ((c = *nptr) && isspace(c)) /* skip leading white space */ X ++nptr; X X if (c == '+' || c == '-') { /* handle signs */ X negative = (c == '-'); X ++nptr; X } X X if (base == 0) { /* determine base if unknown */ X base = 10; X if (*nptr == '0') { X base = 8; X ++nptr; X if ((c = *nptr) == 'x' || c == 'X') { X base = 16; X ++nptr; X } X } X } X else X if (base == 16 && *nptr == '0') { /* discard 0x/0X prefix if hex */ X ++nptr; X if ((c = *nptr == 'x') || c == 'X') X ++nptr; X } X X limit = LONG_MAX / base; /* ensure no overflow */ X X --nptr; /* convert the number */ X while ((c = *++nptr) != 0) { X if (isdigit(c)) X c -= '0'; X else X c -= isupper(c) ? ('A' - 10) : ('a' - 10); X if (c < 0 || c >= base) X break; X saw_a_digit = 1; X if (result > limit) X overflow = 1; X if (!overflow) { X result *= base; X if (c > LONG_MAX - result) X overflow = 1; X else X result += c; X } X } X if (!saw_a_digit) X return 0; X X if (negative && !overflow) X result = 0L - result; X if (overflow) { X errno = ERANGE; X if (negative) X result = LONG_MIN; X else X result = LONG_MAX; X } X X if (endptr != (char **) NULL) /* record good final pointer */ X *endptr = nptr; X return result; X} / echo x - strtoul.c sed '/^X/s///' > strtoul.c << '/' X/* strtoul.c ANSI 4.10.1.6 X * unsigned long int strtoul(const char *nptr, char **endptr, int base); X * X * Converts a numeric string, in various bases, to an unsigned long. X */ X X#include <lib.h> X#include <ctype.h> X#include <errno.h> X#include <limits.h> X#include <stdlib.h> X X#ifdef strtoul X#undef strtoul X#endif X XPUBLIC unsigned long int strtoul(nptr, endptr, base) X_CONST char *nptr; Xchar **endptr; Xint base; X{ X register int c; X unsigned long int result = 0L; X unsigned long int limit; X int negative = 0; X int overflow = 0; X int saw_a_digit = 0; /* it's not a number without a digit */ X X if (endptr != (char **) NULL) /* set up default final pointer */ X *endptr = nptr; X X while ((c = *nptr) && isspace(c)) /* skip leading white space */ X ++nptr; X X if (c == '+' || c == '-') { /* handle signs */ X negative = (c == '-'); X ++nptr; X } X X if (base == 0) { /* determine base if unknown */ X base = 10; X if (*nptr == '0') { X base = 8; X ++nptr; X if ((c = *nptr) == 'x' || c == 'X') { X base = 16; X ++nptr; X } X } X } X else X if (base == 16 && *nptr == '0') { /* discard 0x/0X prefix if hex */ X ++nptr; X if ((c = *nptr == 'x') || c == 'X') X ++nptr; X } X X limit = ULONG_MAX / base; /* ensure no overflow */ X X --nptr; /* convert the number */ X while ((c = *++nptr) != 0) { X if (isdigit(c)) X c -= '0'; X else X c -= isupper(c) ? ('A' - 10) : ('a' - 10); X if (c < 0 || c >= base) X break; X saw_a_digit = 1; X if (result > limit) X overflow = 1; X if (!overflow) { X result = base * result; X if (c > ULONG_MAX - result) X overflow = 1; X else X result += c; X } X } X if (!saw_a_digit) X return 0; X X if (negative && !overflow) /* BIZARRE, but ANSI says we should do this! */ X result = 0L - result; X if (overflow) { X errno = ERANGE; X result = ULONG_MAX; X } X X if (endptr != (char **) NULL) /* record good final pointer */ X *endptr = nptr; X return result; X} /