From c6218dbae579de0cd20f5a7f1e9877673e28225d Mon Sep 17 00:00:00 2001 From: Eric Andersen Date: Wed, 20 Dec 2000 22:52:58 +0000 Subject: A number of updates from Manuel Novoa III. Things look good... --- libc/misc/assert/__assert.c | 19 ++- libc/misc/ctype/ctype.c | 35 +++++- libc/misc/internals/Makefile | 2 +- libc/misc/internals/dtostr.c | 271 +++++++++++++++++++++++++++++++++++++++++ libc/misc/internals/itoa.c | 21 ---- libc/misc/internals/lltostr.c | 39 ++++++ libc/misc/internals/ltoa.c | 40 ------ libc/misc/internals/ltostr.c | 71 +++++------ libc/misc/internals/ulltostr.c | 37 ++++++ libc/misc/internals/ultostr.c | 37 ++++++ libc/misc/internals/zoicheck.c | 16 +++ 11 files changed, 481 insertions(+), 107 deletions(-) create mode 100644 libc/misc/internals/dtostr.c create mode 100644 libc/misc/internals/lltostr.c create mode 100644 libc/misc/internals/ulltostr.c create mode 100644 libc/misc/internals/ultostr.c create mode 100644 libc/misc/internals/zoicheck.c (limited to 'libc/misc') diff --git a/libc/misc/assert/__assert.c b/libc/misc/assert/__assert.c index 905671d98..b98958f43 100644 --- a/libc/misc/assert/__assert.c +++ b/libc/misc/assert/__assert.c @@ -3,11 +3,24 @@ * under the GNU Library General Public License. */ +/* + * Manuel Novoa III Dec 2000 + * + * Converted to use my new (un)signed long (long) to string routines, which + * are smaller than the previous functions and don't require static buffers. + */ + #include #include #include +#include -extern char *itoa(int); +#if (INT_MAX >> 31) +/* We're set up for 32 bit ints */ +#error need to check size allocation for buffer 'buf' +#endif + +extern char *__ltostr(char *buf, unsigned long uval, int base, int uppercase); static void errput(str) const char *str; @@ -21,9 +34,11 @@ const char *filename; int linenumber; const char *function; { + char buf[12]; + errput(filename); errput(":"); - errput(itoa(linenumber)); + errput(__ltostr(buf + sizeof(buf) - 1, linenumber, 10, 0)); errput(function ? ": " : ""); errput(function ? function : ""); errput(function ? "() " : ""); diff --git a/libc/misc/ctype/ctype.c b/libc/misc/ctype/ctype.c index c7cceada6..fb465838f 100644 --- a/libc/misc/ctype/ctype.c +++ b/libc/misc/ctype/ctype.c @@ -9,106 +9,139 @@ #define USE_CTYPE_C_FUNCTIONS #include +#ifdef L_isalnum int isalnum( int c ) { return (isalpha(c) || isdigit(c)); } +#endif +#ifdef L_isalpha int isalpha( int c ) { return (isupper(c) || islower(c)); } +#endif +#ifdef L_isascii int isascii( int c ) { return (c > 0 && c <= 0x7f); } +#endif +#ifdef L_iscntrl int iscntrl( int c ) { return ((c > 0) && ((c <= 0x1f) || (c == 0x7f))); } +#endif +#ifdef L_isdigit int isdigit( int c ) { return (c >= '0' && c <= '9'); } +#endif +#ifdef L_isgraph int isgraph( int c ) { return (c != ' ' && isprint(c)); } +#endif +#ifdef L_islower int islower( int c ) { return (c >= 'a' && c <= 'z'); } +#endif +#ifdef L_isprint int isprint( int c ) { return (c >= ' ' && c <= '~'); } +#endif +#ifdef L_ispunct int ispunct( int c ) { return ((c > ' ' && c <= '~') && !isalnum(c)); } +#endif +#ifdef L_isspace int isspace( int c ) { return (c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v'); } +#endif +#ifdef L_isupper int isupper( int c ) { return (c >= 'A' && c <= 'Z'); } +#endif +#ifdef L_isxdigit int isxdigit( int c ) { return (isxupper(c) || isxlower(c)); } +#endif +#ifdef L_isxlower int isxlower( int c ) { return (isdigit(c) || (c >= 'a' && c <= 'f')); } +#endif +#ifdef L_isxupper int isxupper( int c ) { return (isdigit(c) || (c >= 'A' && c <= 'F')); } +#endif +#ifdef L_toascii int toascii( int c ) { return (c & 0x7f); } +#endif +#ifdef L_tolower int tolower( int c ) { return (isupper(c) ? ( c - 'A' + 'a') : (c)); } +#endif +#ifdef L_toupper int toupper( int c ) { return (islower(c) ? (c - 'a' + 'A') : (c)); } - +#endif diff --git a/libc/misc/internals/Makefile b/libc/misc/internals/Makefile index 38bf7729f..b9168288e 100644 --- a/libc/misc/internals/Makefile +++ b/libc/misc/internals/Makefile @@ -24,7 +24,7 @@ TOPDIR=../../ include $(TOPDIR)Rules.mak LIBC=$(TOPDIR)libc.a -CSRC=itoa.c ltoa.c ltostr.c +CSRC=ultostr.c ltostr.c ulltostr.c lltostr.c zoicheck.c dtostr.c COBJS=$(patsubst %.c,%.o, $(CSRC)) OBJS=$(COBJS) diff --git a/libc/misc/internals/dtostr.c b/libc/misc/internals/dtostr.c new file mode 100644 index 000000000..d9ab80bbb --- /dev/null +++ b/libc/misc/internals/dtostr.c @@ -0,0 +1,271 @@ +/* + * Copyright (C) 2000 Manuel Novoa III + * + * Function: const char *__dtostr(double x) + * + * This was written for uClibc to give it the ability to at least output + * floating point values in exponential form. No formatting is done. + * No trailing 0's are removed. Number of digits generated is not + * runtime selectable. It does however handle +/- infinity and nan on i386. + * + * The goal was usable floating point %e-type output in minimal size. For + * me, "gcc -c -Os -fomit-frame-pointer dtostr.c && size dtostr.o" gives + * text data bss dec hex filename + * 535 9 26 570 23a dtostr.o (WANT_EXP_FORM = 1) + * 530 9 26 565 235 dtostr.o (WANT_EXP_FORM = 0) + * + * Output is of the form [-][#].######{e|E}{+|-}##. Choices of upper or + * lower case exponent character, and initial digit/initial decimal forms + * are compile-time options. Number of digits generated is also selected + * at compile time (MAX_DIGITS). + * + * Notes: + * + * The primary objective of this implementation was minimal size while + * maintaining reasonable accuracy. It should also be fairly portable, + * as not assumptions are made about the bit-layout of doubles. + * + * It should be too difficult to convert this to handle long doubles on i386. + * For information, see the comments below. + * + * There are 2 compile-time options below, as well as some tuning parameters. + * + * TODO: + * long double and/or float version? (note: for float can trim code some). + */ + +/*****************************************************************************/ +/* OPTIONS */ +/*****************************************************************************/ + +/* + * Set this if you want output results with 1 digit before the decimal point. + * If this is 0, all digits follow a leading decimal point. + */ +#define WANT_EXP_FORM 1 + +/* + * Set if you want exponent character 'E' rather than 'e'. + */ +#define EXP_UPPERCASE 1 + +/*****************************************************************************/ +/* Don't change anything that follows unless you know what you're doing. */ +/*****************************************************************************/ + +/* + * Configuration for the scaling power table. Ignoring denormals, you + * should have 2**EXP_TABLE_SIZE >= MAX_DBL_EXP >= 2**(EXP_TABLE_SIZE-1). + * The minimum for standard C is 6. For IEEE 8bit doubles, 9 suffices. + */ +#define EXP_TABLE_SIZE 9 + +/* + * Set this to the maximum number of digits you want converted. + * Conversion is done in blocks of DIGITS_PER_BLOCK (9 by default) digits. + * 17 digits suffices to uniquely determine a double on i386. + */ +#define MAX_DIGITS 17 + +/* + * This is really only used to check for infinities. The macro produces + * smaller code for i386 and, since this is tested before any floating point + * calculations, it doesn't appear to suffer from the excess precision problem + * caused by the FPU that strtod had. If it causes problems, call the function + * and compile zoicheck.c with -ffloat-store. + */ +#if 1 +#define _zero_or_inf_check(x) ( x == (x/4) ) +#else +extern int _zero_or_inf_check(double x); +#endif + +/* + * Fairly portable nan check. Bitwise for i386 generated larger code. + * If you have a better version, comment this out. + */ +#define isnan(x) (x != x) + +/*****************************************************************************/ +/* Don't change anything that follows peroid!!! ;-) */ +/*****************************************************************************/ + +#include +#include + +/* + * Set things up for the scaling power table. + */ + +#if EXP_TABLE_SIZE < 6 +#error EXP_TABLE_SIZE should be at least 6 to comply with standards +#endif + +#define EXP_TABLE_MAX (1U<<(EXP_TABLE_SIZE-1)) + +/* + * Only bother checking if this is too small. + * Throw in some play for denormals ( roughly O(-324) vs O(-307) on i386 ). + */ + +#if (3+DBL_DIG-DBL_MIN_10_EXP)/2 > EXP_TABLE_MAX +#error larger EXP_TABLE_SIZE needed +#endif + +/* + * With 32 bit ints, we can get 9 digits per block. + */ +#define DIGITS_PER_BLOCK 9 + +#if (INT_MAX >> 30) +#define DIGIT_BLOCK_TYPE int +#elif (LONG_MAX >> 30) +#define DIGIT_BLOCK_TYPE long +#else +#error need at least 32 bit longs +#endif + +/* + * This is kind of a place-holder for LONG_DOUBLE support to show what I + * think needs to be changed. I haven't tried it though. Changing this + * from 3 to 4 and converting double to long double should work on i386. + * DON'T FORGET to increase EXP_TABLE_SIZE and MAX_DIGITS. + * DON'T FORGET the "larger EXP_TABLE_SIZE needed" check above. + */ +#define MAX_EXP_DIGITS 3 + +/*****************************************************************************/ + +#define NUM_DIGIT_BLOCKS ((MAX_DIGITS+DIGITS_PER_BLOCK-1)/DIGITS_PER_BLOCK) + +static char infstr[] = " inf"; /* save space for a - sign */ +static char nanstr[] = "nan"; + +/* extra space for '-', '.', 'e+###', and nul */ +/*static char buf[ 5 + MAX_EXP_DIGITS + NUM_DIGIT_BLOCKS * DIGITS_PER_BLOCK];*/ +#define BUF_SIZE ( 5 + MAX_EXP_DIGITS + NUM_DIGIT_BLOCKS * DIGITS_PER_BLOCK ) +/*****************************************************************************/ + +const char *__dtostr(char *buf, double x) +{ + double exp_table[EXP_TABLE_SIZE]; + double p10; + DIGIT_BLOCK_TYPE digit_block; /* int of at least 32 bits */ + int i, j; + int exp, exp_neg; + int negative = 0; + char *pos; + + if (isnan(x)) { /* nan check */ + return nanstr; + } + + if (x == 0) { /* handle 0 now to avoid false positive */ + exp = 0; /* with inf test, and to avoid scaling */ + goto GENERATE_DIGITS; /* note: time vs space tradeoff */ + } + + if (x < 0) { /* convert negatives to positives */ + negative = 1; + x = -x; + } + + if (_zero_or_inf_check(x)) { /* must be inf since zero handled above */ + pos = infstr + 1; + goto DO_SIGN; + } + + /* need to build the scaling table */ + for (i = 0, p10 = 10 ; i < EXP_TABLE_SIZE ; i++) { + exp_table[i] = p10; + p10 *= p10; + } + + exp_neg = 0; + if (x < 1e8) { /* do we need to scale up or down? */ + exp_neg = 1; + } + +#if WANT_EXP_FORM + exp = DIGITS_PER_BLOCK - 1; +#else + exp = DIGITS_PER_BLOCK; +#endif + + i = EXP_TABLE_SIZE; + j = EXP_TABLE_MAX; + while ( i-- ) { /* scale x such that 1e8 <= x < 1e9 */ + if (exp_neg) { + if (x * exp_table[i] < 1e9) { + x *= exp_table[i]; + exp -= j; + } + } else { + if (x / exp_table[i] >= 1e8) { + x /= exp_table[i]; + exp += j; + } + } + j >>= 1; + } + + GENERATE_DIGITS: + pos = buf - BUF_SIZE + 1 + DIGITS_PER_BLOCK + 1; /* leave space for '.' and - */ + + for (i = 0 ; i < NUM_DIGIT_BLOCKS ; ++i ) { + digit_block = (int) x; + x = (x - digit_block) * 1e9; + for (j = 0 ; j < DIGITS_PER_BLOCK ; j++) { + *--pos = '0' + (digit_block % 10); + digit_block /= 10; + } + pos += (2*DIGITS_PER_BLOCK); + } + pos -= (DIGITS_PER_BLOCK*(NUM_DIGIT_BLOCKS+1))-MAX_DIGITS; + + /* start generating the exponent */ +#if EXP_UPPERCASE + *pos = 'E'; +#else + *pos = 'e'; +#endif + *++pos = '+'; + if (exp < 0) { + *pos = '-'; + exp = -exp; + } + pos += 3; /* WARNING: Assumes max exp < 1000!!! */ + if (exp >= 100) { + ++pos; +#if MAX_EXP_DIGITS > 4 +#error need to modify exponent string generation code +#elif MAX_EXP_DIGITS > 3 + if (exp >= 1000) { /* WARNING: hasn't been checked */ + ++pos; /* but should work */ + } +#endif + } + *pos = '\0'; + + for (j = 0 ; (j < 2) || exp ; j++) { /* standard says at least 2 digits */ + *--pos = '0' + (exp % 10); + exp /= 10; + } + + /* insert the decimal point */ + pos = buf - BUF_SIZE + 1; + +#if WANT_EXP_FORM + *pos = *(pos+1); + *(pos+1) = '.'; +#else + *pos = '.'; +#endif + + DO_SIGN: + if (negative) { + *--pos = '-'; + } + + return pos; +} diff --git a/libc/misc/internals/itoa.c b/libc/misc/internals/itoa.c index a683b8018..e69de29bb 100644 --- a/libc/misc/internals/itoa.c +++ b/libc/misc/internals/itoa.c @@ -1,21 +0,0 @@ -/* itoa.c */ -#define __MAX_INT_CHARS 7 - -char *itoa(int i) -{ - static char a[__MAX_INT_CHARS]; - char *b = a + sizeof(a) - 1; - int sign = (i < 0); - - if (sign) - i = -i; - *b = 0; - do { - *--b = '0' + (i % 10); - i /= 10; - } - while (i); - if (sign) - *--b = '-'; - return b; -} diff --git a/libc/misc/internals/lltostr.c b/libc/misc/internals/lltostr.c new file mode 100644 index 000000000..2ce359615 --- /dev/null +++ b/libc/misc/internals/lltostr.c @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2000 Manuel Novoa III + * + * Note: buf is a pointer to the END of the buffer passed. + * Call like this: + * char buf[SIZE], *p; + * p = __lltostr(buf + sizeof(buf) - 1, ...) + * For long longs of 64 bits, appropriate buffer sizes are: + * base = 2 66 = 1 (possible -) sign + 64 digits + 1 nul + * base = 10 21 = 1 (possible -) sign + 19 digits + 1 nul + * base = 16 18 = 1 (possible -) sign + 16 hex digits + 1 nul + */ + +extern char *__ulltostr(char *buf, unsigned long long uval, int base, + int uppercase); + +char *__lltostr(char *buf, long long val, int base, int uppercase) +{ + unsigned long long uval; + char *pos; + int negative; + + negative = 0; + if (val < 0) { + negative = 1; + uval = ((unsigned long long)(-(1+val))) + 1; + } else { + uval = val; + } + + + pos = __ulltostr(buf, uval, base, uppercase); + + if (pos && negative) { + *--pos = '-'; + } + + return pos; +} diff --git a/libc/misc/internals/ltoa.c b/libc/misc/internals/ltoa.c index da8b6d3df..e69de29bb 100644 --- a/libc/misc/internals/ltoa.c +++ b/libc/misc/internals/ltoa.c @@ -1,40 +0,0 @@ -/* Copyright (C) 1995,1996 Robert de Bath - * This file is part of the Linux-8086 C library and is distributed - * under the GNU Library General Public License. - */ - -static char buf[12]; - -extern char *ultoa(); - -char *ltoa(val) -long val; -{ - char *p; - int flg = 0; - - if (val < 0) { - flg++; - val = -val; - } - p = ultoa(val); - if (flg) - *--p = '-'; - return p; -} - -char *ultoa(val) -unsigned long val; -{ - char *p; - - p = buf + sizeof(buf); - *--p = '\0'; - - do { - *--p = '0' + val % 10; - val /= 10; - } - while (val); - return p; -} diff --git a/libc/misc/internals/ltostr.c b/libc/misc/internals/ltostr.c index da6fad232..7e45fec30 100644 --- a/libc/misc/internals/ltostr.c +++ b/libc/misc/internals/ltostr.c @@ -1,52 +1,39 @@ -/* Copyright (C) 1995,1996 Robert de Bath - * This file is part of the Linux-8086 C library and is distributed - * under the GNU Library General Public License. +/* + * Copyright (C) 2000 Manuel Novoa III + * + * Note: buf is a pointer to the END of the buffer passed. + * Call like this: + * char buf[SIZE], *p; + * p = __ltostr(buf + sizeof(buf) - 1, ...) + * + * For longs of 32 bits, appropriate buffer sizes are: + * base = 2 34 = 1 (possible -) sign + 32 digits + 1 nul + * base = 10 12 = 1 (possible -) sign + 10 digits + 1 nul + * base = 16 10 = 1 (possible -) sign + 8 hex digits + 1 nul */ -static char buf[34]; +extern char *__ultostr(char *buf, unsigned long uval, int base, int uppercase); -extern char *ultostr(); - -char *ltostr(val, radix, uppercase) -long val; -int radix; -int uppercase; +char *__ltostr(char *buf, long val, int base, int uppercase) { - char *p; - int flg = 0; - - if (val < 0) { - flg++; - val = -val; + unsigned long uval; + char *pos; + int negative; + + negative = 0; + if (val < 0) { + negative = 1; + uval = ((unsigned long)(-(1+val))) + 1; + } else { + uval = val; } - p = ultostr(val, radix, uppercase); - if (p && flg) - *--p = '-'; - return p; -} -char *ultostr(val, radix, uppercase) -unsigned long val; -int radix; -int uppercase; -{ - register char *p; - register int c; - if (radix > 36 || radix < 2) - return 0; + pos = __ultostr(buf, uval, base, uppercase); - p = buf + sizeof(buf); - *--p = '\0'; + if (pos && negative) { + *--pos = '-'; + } - do { - c = val % radix; - val /= radix; - if (c > 9) - *--p = (uppercase ? 'A' : 'a') - 10 + c; - else - *--p = '0' + c; - } - while (val); - return p; + return pos; } diff --git a/libc/misc/internals/ulltostr.c b/libc/misc/internals/ulltostr.c new file mode 100644 index 000000000..50246d3bc --- /dev/null +++ b/libc/misc/internals/ulltostr.c @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2000 Manuel Novoa III + * + * Note: buf is a pointer to the END of the buffer passed. + * Call like this: + * char buf[SIZE], *p; + * p = __ulltostr(buf + sizeof(buf) - 1, ...) + * + * For long longs of 64 bits, appropriate buffer sizes are: + * base = 2 65 = 64 digits + 1 nul + * base = 10 20 = 19 digits + 1 nul + * base = 16 17 = 16 hex digits + 1 nul + */ + +char *__ulltostr(char *buf, unsigned long long uval, int base, int uppercase) +{ + int digit; + + if ((base < 2) || (base > 36)) { + return 0; + } + + *buf = '\0'; + + do { + digit = uval % base; + uval /= base; + + /* note: slightly slower but generates less code */ + *--buf = '0' + digit; + if (digit > 9) { + *buf = (uppercase ? 'A' : 'a') + digit - 10; + } + } while (uval); + + return buf; +} diff --git a/libc/misc/internals/ultostr.c b/libc/misc/internals/ultostr.c new file mode 100644 index 000000000..d4663fb39 --- /dev/null +++ b/libc/misc/internals/ultostr.c @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2000 Manuel Novoa III + * + * Note: buf is a pointer to the END of the buffer passed. + * Call like this; + * char buf[SIZE], *p; + * p = __ultostr(buf + sizeof(buf) - 1, ...) + * + * For longs of 32 bits, appropriate buffer sizes are: + * base = 2 33 = 32 digits + 1 nul + * base = 10 11 = 10 digits + 1 nul + * base = 16 9 = 8 hex digits + 1 nul + */ + +char *__ultostr(char *buf, unsigned long uval, int base, int uppercase) +{ + int digit; + + if ((base < 2) || (base > 36)) { + return 0; + } + + *buf = '\0'; + + do { + digit = uval % base; + uval /= base; + + /* note: slightly slower but generates less code */ + *--buf = '0' + digit; + if (digit > 9) { + *buf = (uppercase ? 'A' : 'a') + digit - 10; + } + } while (uval); + + return buf; +} diff --git a/libc/misc/internals/zoicheck.c b/libc/misc/internals/zoicheck.c new file mode 100644 index 000000000..2113c6298 --- /dev/null +++ b/libc/misc/internals/zoicheck.c @@ -0,0 +1,16 @@ + +/* + * Copyright (C) 2000 Manuel Novoa III + * + * This is a utility routine for strtod errno support. + * As the name implies, it checks if a double is either 0 or +/-infinity. + * Doing this inline doesn't work on i386 because of excess precission + * stored in the FPU. + * + * TODO: Check bitmasks directly? + */ + +int _zero_or_inf_check(double x) +{ + return ( x == x/4 ); +} -- cgit v1.2.3