diff options
author | Manuel Novoa III <mjn3@codepoet.org> | 2002-03-12 01:18:50 +0000 |
---|---|---|
committer | Manuel Novoa III <mjn3@codepoet.org> | 2002-03-12 01:18:50 +0000 |
commit | 03e039820dc5092e27e81f3671652f25da7f25f1 (patch) | |
tree | 37bddad6951b8a6aa5d75184353705f672217812 /libc/misc/internals/dtostr.c | |
parent | ff3e48d94097ed02480bb0df538620b221ccd72f (diff) |
Swap in the new stdio code.
Diffstat (limited to 'libc/misc/internals/dtostr.c')
-rw-r--r-- | libc/misc/internals/dtostr.c | 423 |
1 files changed, 0 insertions, 423 deletions
diff --git a/libc/misc/internals/dtostr.c b/libc/misc/internals/dtostr.c deleted file mode 100644 index 85ec69b12..000000000 --- a/libc/misc/internals/dtostr.c +++ /dev/null @@ -1,423 +0,0 @@ -/* - * Copyright (C) 2000, 2001 Manuel Novoa III - * - * Function: int __dtostr(FILE * fp, size_t size, long double x, - * char flag[], int width, int preci, char mode) - * - * This was written for uClibc to provide floating point support for - * the printf functions. It handles +/- infinity and nan on i386. - * - * Notes: - * - * At most MAX_DIGITS significant digits are kept. Any trailing digits - * are treated as 0 as they are really just the results of rounding noise - * anyway. If you want to do better, use an arbitary precision arithmetic - * package. ;-) - * - * 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. - * - * TODO: - * long double and/or float version? (note: for float can trim code some). - * - * Decrease the size. This is really much bigger than I'd like. - */ - -/*****************************************************************************/ -/* 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 >= LDBL_MAX_EXP >= 2**(EXP_TABLE_SIZE-1). - * The minimum for standard C is 6. For IEEE 8bit doubles, 9 suffices. - * For long doubles on i386, use 13. - */ -#define EXP_TABLE_SIZE 13 - -/* - * Set this to the maximum number of digits you want converted. - * Conversion is done in blocks of DIGITS_PER_BLOCK (9 by default) digits. - * (20) 17 digits suffices to uniquely determine a (long) double on i386. - */ -#define MAX_DIGITS 20 - -/* - * Set this to the smallest integer type capable of storing a pointer. - */ -#define INT_OR_PTR int - -/* - * 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. - */ -#define _zero_or_inf_check(x) ( x == (x/4) ) - -/* - * 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 <stdio.h> -#include <string.h> -#include <assert.h> -#include <float.h> -#include <limits.h> - -extern int fnprintf(FILE * fp, size_t size, const char *fmt, ...); - -/* from printf.c -- should really be in an internal header file */ -enum { - FLAG_PLUS = 0, - FLAG_MINUS_LJUSTIFY, - FLAG_HASH, - FLAG_0_PAD, - FLAG_SPACE, -}; - -/*****************************************************************************/ - -/* - * 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. - */ - -#if LDBL_MAX_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 -#define DB_FMT "%.*d" -#elif (LONG_MAX >> 30) -#define DIGIT_BLOCK_TYPE long -#define DB_FMT "%.*ld" -#else -#error need at least 32 bit longs -#endif - -/* Are there actually any machines where this might fail? */ -#if 'A' > 'a' -#error ordering assumption violated : 'A' > 'a' -#endif - -/* Maximum number of calls to fnprintf to output double. */ -#define MAX_CALLS 8 - -/*****************************************************************************/ - -#define NUM_DIGIT_BLOCKS ((MAX_DIGITS+DIGITS_PER_BLOCK-1)/DIGITS_PER_BLOCK) - -/* extra space for '-', '.', 'e+###', and nul */ -#define BUF_SIZE ( 3 + NUM_DIGIT_BLOCKS * DIGITS_PER_BLOCK ) -/*****************************************************************************/ - -static const char *fmts[] = { - "%0*d", "%.*s", ".", "inf", "INF", "nan", "NAN", "%*s" -}; - -/*****************************************************************************/ - -int __dtostr(FILE * fp, size_t size, long double x, - char flag[], int width, int preci, char mode) -{ - long double exp_table[EXP_TABLE_SIZE]; - long double p10; - DIGIT_BLOCK_TYPE digit_block; /* int of at least 32 bits */ - int i, j; - int round, o_exp; - int exp, exp_neg; - char *s; - char *e; - char buf[BUF_SIZE]; - INT_OR_PTR pc_fwi[2*MAX_CALLS]; - INT_OR_PTR *ppc; - char exp_buf[8]; - char drvr[8]; - char *pdrvr; - int npc; - int cnt; - char sign_str[2]; - char o_mode; - - /* check that INT_OR_PTR is sufficiently large */ - assert( sizeof(INT_OR_PTR) == sizeof(char *) ); - - *sign_str = flag[FLAG_PLUS]; - *(sign_str+1) = 0; - if (isnan(x)) { /* nan check */ - pdrvr = drvr + 1; - *pdrvr++ = 5 + (mode < 'a'); - pc_fwi[2] = 3; - flag[FLAG_0_PAD] = 0; - goto EXIT_SPECIAL; - } - - if (x == 0) { /* handle 0 now to avoid false positive */ - exp = -1; - goto GENERATE_DIGITS; - } - - if (x < 0) { /* convert negatives to positives */ - *sign_str = '-'; - x = -x; - } - - if (_zero_or_inf_check(x)) { /* must be inf since zero handled above */ - pdrvr = drvr + 1; - *pdrvr++ = 3 + + (mode < 'a'); - pc_fwi[2] = 3; - flag[FLAG_0_PAD] = 0; - goto EXIT_SPECIAL; - } - - /* 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; - } - - exp = DIGITS_PER_BLOCK - 1; - - 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; - } - if (x >= 1e9) { /* handle bad rounding case */ - x /= 10; - ++exp; - } - assert(x < 1e9); - - GENERATE_DIGITS: - s = buf + 2; /* leave space for '\0' and '0' */ - - for (i = 0 ; i < NUM_DIGIT_BLOCKS ; ++i ) { - digit_block = (DIGIT_BLOCK_TYPE) x; - x = (x - digit_block) * 1e9; - s += sprintf(s, DB_FMT, DIGITS_PER_BLOCK, digit_block); - } - - /*************************************************************************/ - - *exp_buf = 'e'; - if (mode < 'a') { - *exp_buf = 'E'; - mode += ('a' - 'A'); - } - - o_mode = mode; - - round = preci; - - if ((mode == 'g') && (round > 0)){ - --round; - } - - if (mode == 'f') { - round += exp; - } - - s = buf; - *s++ = 0; /* terminator for rounding and 0-triming */ - *s = '0'; /* space to round */ - - i = 0; - e = s + MAX_DIGITS + 1; - if (round < MAX_DIGITS) { - e = s + round + 2; - if (*e >= '5') { - i = 1; - } - } - - do { /* handle rounding and trim trailing 0s */ - *--e += i; /* add the carry */ - } while ((*e == '0') || (*e > '9')); - - o_exp = exp; - if (e <= s) { /* we carried into extra digit */ - ++o_exp; - e = s; /* needed if all 0s */ - } else { - ++s; - } - *++e = 0; /* ending nul char */ - - if ((mode == 'g') && ((o_exp >= -4) && (o_exp <= round))) { - mode = 'f'; - } - - exp = o_exp; - if (mode != 'f') { - o_exp = 0; - } - - if (o_exp < 0) { - *--s = '0'; /* fake the first digit */ - } - - pdrvr = drvr+1; - ppc = pc_fwi+2; - - *pdrvr++ = 0; - *ppc++ = 1; - *ppc++ = (INT_OR_PTR)(*s++ - '0'); - - i = e - s; /* total digits */ - if (o_exp >= 0) { - if (o_exp >= i) { /* all digit(s) left of decimal */ - *pdrvr++ = 1; - *ppc++ = i; - *ppc++ = (INT_OR_PTR)(s); - o_exp -= i; - i = 0; - if (o_exp>0) { /* have 0s left of decimal */ - *pdrvr++ = 0; - *ppc++ = o_exp; - *ppc++ = 0; - } - } else if (o_exp > 0) { /* decimal between digits */ - *pdrvr++ = 1; - *ppc++ = o_exp; - *ppc++ = (INT_OR_PTR)(s); - s += o_exp; - i -= o_exp; - } - o_exp = -1; - } - - if (flag[FLAG_HASH] || (i) || ((o_mode != 'g') && (preci > 0))) { - *pdrvr++ = 2; /* need decimal */ - *ppc++ = 1; /* needed for width calc */ - ppc++; - } - - if (++o_exp < 0) { /* have 0s right of decimal */ - *pdrvr++ = 0; - *ppc++ = -o_exp; - *ppc++ = 0; - } - if (i) { /* have digit(s) right of decimal */ - *pdrvr++ = 1; - *ppc++ = i; - *ppc++ = (INT_OR_PTR)(s); - } - - if (o_mode != 'g') { - i -= o_exp; - if (i < preci) { /* have 0s right of digits */ - i = preci - i; - *pdrvr++ = 0; - *ppc++ = i; - *ppc++ = 0; - } - } - - /* build exponent string */ - if (mode != 'f') { - *pdrvr++ = 1; - *ppc++ = sprintf(exp_buf,"%c%+.2d", *exp_buf, exp); - *ppc++ = (INT_OR_PTR) exp_buf; - } - - EXIT_SPECIAL: - npc = pdrvr - drvr; - ppc = pc_fwi + 2; - for (i=1 ; i< npc ; i++) { - width -= *(ppc++); - ppc++; - } - i = 0; - if (*sign_str) { - i = 1; - } - width -= i; - if (width <= 0) { - width = 0; - } else { - if (flag[FLAG_MINUS_LJUSTIFY]) { /* padding on right */ - ++npc; - *pdrvr++ = 7; - *ppc = width; - *++ppc = (INT_OR_PTR)(""); - width = 0; - } else if (flag[FLAG_0_PAD] == '0') { /* 0 padding */ - pc_fwi[2] += width; - width = 0; - } - } - *drvr = 7; - ppc = pc_fwi; - *ppc++ = width + i; - *ppc = (INT_OR_PTR) sign_str; - - pdrvr = drvr; - ppc = pc_fwi; - cnt = 0; - for (i=0 ; i<npc ; i++) { -#if 1 - fnprintf(fp, size, fmts[(int)(*pdrvr++)], (INT_OR_PTR)(*(ppc)), - (INT_OR_PTR)(*(ppc+1))); -#else - j = fnprintf(fp, size, fmts[(int)(*pdrvr++)], (INT_OR_PTR)(*(ppc)), - (INT_OR_PTR)(*(ppc+1))); - assert(j == *ppc); -#endif - if (size > *ppc) { - size -= *ppc; - } - cnt += *ppc; /* to avoid problems if j == -1 */ - ppc += 2; - } - - return cnt; -} - - - - - - |