📄 vfprintf.c
字号:
} while (_ulong); /* handle octal leading 0 */ if (is_flag(ALT) && *cp != '0') *--cp = '0'; break; case DEC:#if defined(__TCS__)/* * Integer divide and remainder are very inefficient on TM-1, * so we try to avoid them by using custom ops. * Use umulm() to multiply by 2^32/10 (rounded up to 0x1999999A) * and take the high order word of the 64-bit result to get _ulong / 10; * similarly for remainder. * This does not work for very large _ulong because of the rounding, * so we just use normal / and % for those cases (once). */ if (_ulong >= 0x40000000) { /* Use / and % at most once. */ *--cp = _ulong % 10 + '0'; _ulong /= 10; } /* Many numbers are 1 digit. */ while (_ulong >= 10) { unsigned long udiv10; udiv10 = umulm(_ulong, 0x1999999A); *--cp = umulm(10, _ulong * 0x1999999A) + '0'; _ulong = udiv10; } *--cp = _ulong + '0'; break;#else /* defined(__TCS__) */ /* Many numbers are 1 digit. */ while (_ulong >= 10) { *--cp = _ulong % 10 + '0'; _ulong /= 10; } *--cp = _ulong + '0'; break;#endif /* defined(__TCS__) */ case HEX: do { *--cp = xdigs[_ulong & 15]; _ulong >>= 4; } while (_ulong); break; } } size = buf + NBUF - cp; skipsize: break; default: /* "%?" prints ?, unless ? is NUL */ if (ch == '\0') goto done; /* pretend it was %c with argument ch */ cp = buf; *cp = ch; size = 1; sign = 0; break; } /* * All reasonable formats wind up here. At this point, * `cp' points to a string of length size which (if !LADJUST) * should be padded out to `width' places. If ZEROPAD, * it should first be prefixed by any * sign or other prefix; otherwise, it should be blank * padded before the prefix is emitted. After any * left-hand padding and prefixing, emit zeroes * required by a decimal [diouxX] precision, then print * the string proper, then emit zeroes required by any * leftover floating precision and print the floating exponent; * finally, if LADJUST, pad with blanks. */ /* * compute actual size, so we know how much to pad. * fieldsz excludes decimal prec; realsz includes it */ fieldsz = size + expsize + fppad; if (sign) { if (dprec) dprec++; fieldsz++; } else if (is_flag(HEXPREFIX)) fieldsz += 2; realsz = dprec > fieldsz ? dprec : fieldsz; /* right-adjusting blank padding */ if (!is_flag(LADJUST) && !is_flag(ZEROPAD)) PAD(width - realsz, blanks); /* prefix */ if (sign) { PRINT(&sign, 1); } else if (is_flag(HEXPREFIX)) { ox[0] = '0'; ox[1] = ch; PRINT(ox, 2); } /* right-adjusting zero padding */ if (is_flag(ZEROPAD) && !is_flag(LADJUST)) PAD(width - realsz, zeroes); /* leading zeroes from decimal precision */ PAD(dprec - fieldsz, zeroes); /* the string or number proper */ PRINT(cp, size); /* trailing f.p. zeroes */ PAD(fppad, zeroes); /* trailing f.p. exponent */ PRINT(expbuf, expsize); /* left-adjusting padding (always blank) */ if (is_flag(LADJUST)) PAD(width - realsz, blanks); /* finally, adjust ret */ ret += width > realsz ? width : realsz; FLUSH(); /* copy out the I/O vectors */ }done: FLUSH();error: result= (__sferror(stream)) ? EOF : ret; EXCL_END(&stream->_file_lock); return result;}/* * This routine does all the significant formatting for floating point output, * leaving it up to the caller to provide padding and output the result. * Convert double d with given format, precision and flags into buf. * Store the sign through signp, the exponent (if any) in expbuf, * the exponent size (if any) through expsizep, * and the floating point 0-padding required through fppadp. * The 0-padding is extra precision required by the precision field; it is * not part of the returned buffer because prec might be arbitrarily large. * The exponent is not part of the returned buffer because the extra 0-padding * must precede the exponent in the actual output. * Return the number of characters written to buf, not including expsize. * * Exceptions: * "NaN" for NaNs * "+Inf" for +Infinity * "-Inf" for -Infinity * "-0" for minus zero * * Note: this currently prints IEEE minus zero * and values which round to -0 with '-' sign, * e.g. ``printf("%.3f", -.0001);'' prints "-0.000". * This may or may not be desirable. * To change this behavior, pass a different signp (not &sign) * in the fpcvt() call from vfprintf(), pass signp down to significand() * so significand() can zap the minus sign if the result rounds to 0, * and resolve the new *signp with sign in vfprintf(). * The separate signp would be needed to avoid zapping * a specified '+' or ' ' flag in vfprintf(). */staticintfpcvt(double d, char *buf, int fmtch, int prec, int flags, char *signp, char *expbuf, int *expsizep, int *fppadp){ register int digits; register char *cp, *s, *expp; int decexp, gformat; char digbuf[MAXPREC];#if defined(__IEEE_FP__) /* Deal with NaN and infinities. */ if (_isNaN(d)) { strcpy(buf, "NaN"); return 3; } else if (_isInfinity(d)) { strcpy(buf, (d > 0) ? "+Inf" : "-Inf"); return 4; } else if (_ismZero(d)) { /* print minus zero with '-' */ d = -d; *signp = '-'; }#endif /* defined(__IEEE_FP__) */ /* Force d nonnegative. */ if (d < 0) { *signp = '-'; d = -d; } s = buf; /* destination */ cp = digbuf; /* source */ gformat = (fmtch == 'g' || fmtch == 'G'); /* Check for default precision. */ if (prec == -1) prec = DEFPREC; /* -1 means use default */ else if (prec == 0 && gformat) prec = 1; /* because ISO 9899-1990 says so */ /* Find the significant digits in d. */ digits = significand(d, fmtch, prec, digbuf, &decexp); /* Convert the number to the desired format. */ switch(fmtch) { case 'e': case 'E':e_or_g: /* * Store the exponent into expbuf and its length to *expsizep. * N.B. the code below assumes decexp < 1000. */ expp = expbuf; *expp++ = fmtch; /* 'E' or 'e' */ if (decexp < 0) { /* '+' or '-' */ decexp = -decexp; *expp++ = '-'; } else *expp++ = '+'; if (decexp >= 100) { *expp++ = (decexp / 100) + '0'; decexp %= 100; } *expp++ = (decexp / 10) + '0'; /* at least two digits mandated */ decexp %= 10; *expp++ = decexp + '0'; *expsizep = expp - expbuf; /* Store the first significand digit and decimal point. */ *s++ = *cp++; /* digit preceding '.' */ --digits; if (is_flag(ALT) || (prec > 0 && (!gformat || digits > 0))) *s++ = '.'; break; case 'g': case 'G': if (decexp < -4 || decexp >= prec) { /* * Use 'e' or 'E' format. * 'e' precision means digits after the '.', but * 'g' precision means significant digits; * hence the "--prec;" below, since there is one * significant digit before the '.'. */ fmtch -= 2; /* 'G'->'E', 'g'->'e' */ --prec; /* adjust precision */ goto e_or_g; } /* FALLTHROUGH */ /* use 'f' format. */ case 'f': if (decexp >= 0) { for ( ; digits > 0 && decexp >= 0; --digits, --decexp) { *s++ = *cp++; /* store digit before '.' */ if (gformat) --prec; } for ( ; decexp >= 0; --decexp) { *s++ = '0'; /* out of digits, store a significant 0 */ if (gformat) --prec; } } else *s++ = '0'; /* store insignificant '0' before '.' */ /* Need decimal point if precision remains or alt flag. */ if (is_flag(ALT) || (prec > 0 && (!gformat || digits > 0))) *s++ = '.'; /* store decimal point */ for ( ; decexp < -1 && prec > 0; ++decexp) { *s++ = '0'; /* store insignificant '0' after '.' */ if (!gformat) --prec; } break; } /* Store the remainder of the significand and pad later as needed. */ for ( ; digits > 0 && prec > 0; --digits, --prec) *s++ = *cp++; /* store digits after '.' */ if (prec > 0 && (!gformat || is_flag(ALT))) *fppadp += prec; /* pad with more 0s later */ return s - buf;}/* * This routine does the serious work of fp output conversion: * it produces the sequence of significant digits of the result, * not yet formatted. * Given a nonnegative double d and a desired format and precision, * convert d into a nonempty string of decimal digits in the * supplied buffer and store the corresponding decimal exponent in *expp. * Round the result appropriately and return the length of the result string. * The returned string has neither leading nor trailing zeros (or is "0") * and is at most MAXPREC characters long. * It has an implicit decimal point after the first digit. */staticintsignificand(double d, int fmtch, int prec, char *buf, int *expp){ register char *cp; register int i, digits; cp = buf; if (d == 0.0) { /* just return "0" */zero: *cp = '0'; *expp = 0; return 1; } /* * Find the decimal exponent and reduce d to [1., 10). * N.B. *expp may be adjusted below if the result rounds up. * We need to know the decimal exponent in order to decide * how many significant digits will be required for 'f' format. */ d = frexp10(d, expp); /* * Compute the number of significant digits needed, depending on the format. * Note that the number of significant digits needed must be positive * for 'e' (because prec >= 0 and digits = 1 + prec) * and for 'g' (because prec > 0 and digits = prec), * but may be zero or negative for 'f' when converting * a value with a negative decimal exponent. * Digits after MAXPREC significant digits are noise, do not convert them. * The last of the MAXPREC digits may be partially noise but * we convert it anyway rather than throw away some valid precision. */ if (fmtch == 'e' || fmtch == 'E') digits = 1 + prec; /* e.g. 1.234 */ else if (fmtch == 'f') digits = *expp + 1 + prec; /* e.g. 123.456 */ else digits = prec; /* 'g' or 'G' format */ if (digits > MAXPREC) digits = MAXPREC; /* convert at most MAXPREC digits */ /* Check for zero digits or negative digits ('f' format). */ if (digits <= 0) { if (digits == 0 && d >= 5.0) { /* round up to return "1" */ *cp = '1'; ++*expp; /* adjust decimal exponent */ return 1; } else goto zero; /* return "0" */ } /* Store result digits. */ while (cp < &buf[digits] && d != 0) { i = (int)d; *cp++ = i + '0'; #if 0 _write(1, "[", 1); _write(1, cp - 1, 1); _write(1, "]", 1); #endif d = 10.0 * (d - (double)i); /* reduce d for next digit */ } /* Round the result and strip trailing zeros. */ if (d < 5.0) { /* do not round up */ while (cp > &buf[1] && *(cp - 1) == '0') --cp; /* skip a trailing zero */ return cp - buf; /* return result length */ } else { /* round up */ while (--cp >= buf) /* look at previous digit */ if (++*cp <= '9') /* bump it */ return cp + 1 - buf; /* done */ /* * cp == &buf[-1], so we rounded e.g. "99" to "00". * Adjust the decimal exponent and return "1". */ ++*expp; /* bump decimal exponent */ *++cp = '1'; /* return "1" */ return 1; } /* NOTREACHED */}/* * frexp10() is like frexp() with a decimal rather than a binary base. * For nonzero d, it returns a double decfrac in the range [+-][1., 10) and * stores a decimal exponent through *expp, so d = decfrac * 10 ^ (*expp). * It extracts the decimal exponent fairly efficiently by using frexp() * and scales d with one multiplication and possibly one division. * Useful equations: * log10(d) = log10(decfrac) + decexp, where log10(decfrac) is in [0., 1.); * log10(d) = log2(d) / log2(10); * d = binfrac * 2 ^ binexp, so log2(d) = log2(binfrac) + binexp. * If binfrac and binexp are the results of frexp(), * then binfrac is in [.5, 1.) and log2(binfrac) is in [-1., 0). * Together these imply that (binexp - 1 / log2(10)) is a good lower bound * for decexp, off by at most 1. * * Exceptions: * Error Return Store Condition * none 0.0 0.0 d is 0.0 * EDOM NaN NaN d is NaN * ERANGE [+-]1.0 INT_MAX d is [+-]Infinity */staticdoublefrexp10(double d, int *expp){ register decexp; int binexp; double dexp;#if 0 /* * The code here to do NaNs, infinities and 0.0 in not needed, * because fpcvt() checks for NaNs and infinities * and significand() checks for 0.0 before frexp10() gets called. */#if defined(__IEEE_FP__) if (_isNaN(d)) { errno = EDOM; *expp = 0.0; return d; } else if (_isInfinity(d)) { errno = ERANGE; *expp = INT_MAX; return (d < 0.0) ? -1.0 : 1.0; }#endif /* defined(__IEEE_FP__) */ if (d == 0.0) { *expp = d; return d; }#endif /* 0 */ (void)frexp(d, &binexp); /* compute binary exponent */ if (modf((--binexp) / LOG2_10, &dexp) < 0.0) /* get decimal exponent */ dexp -= 1.0; /* adjust if negative */ decexp = (int)dexp; /* convert to int */ /* * Scale d to desired range. * Avoid using _pow10(decexp) for decexp < 0 to avoid imprecision. */ if (decexp > 0) d /= _pow10(decexp); else if (decexp < 0) d *= _pow10(-decexp); if (d >= 10.) { /* missed by 1, adjust */ ++decexp; d /= 10.; /* not "d *= .1;" for precision */ } /* Store the decimal exponent and return. */ *expp = decexp; return d;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -