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}
/