This source file includes following definitions.
- pow10
- xadd1
- xsub1
- xfmtexp
- xdtoa
- __efgfmt
#include <u.h>
#include <errno.h>
#include <libc.h>
#include "fmtdef.h"
enum
{
FDIGIT = 30,
FDEFLT = 6,
NSIGNIF = 17
};
static double pows10[] =
{
1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,
1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
1e20, 1e21, 1e22, 1e23, 1e24, 1e25, 1e26, 1e27, 1e28, 1e29,
1e30, 1e31, 1e32, 1e33, 1e34, 1e35, 1e36, 1e37, 1e38, 1e39,
1e40, 1e41, 1e42, 1e43, 1e44, 1e45, 1e46, 1e47, 1e48, 1e49,
1e50, 1e51, 1e52, 1e53, 1e54, 1e55, 1e56, 1e57, 1e58, 1e59,
1e60, 1e61, 1e62, 1e63, 1e64, 1e65, 1e66, 1e67, 1e68, 1e69,
1e70, 1e71, 1e72, 1e73, 1e74, 1e75, 1e76, 1e77, 1e78, 1e79,
1e80, 1e81, 1e82, 1e83, 1e84, 1e85, 1e86, 1e87, 1e88, 1e89,
1e90, 1e91, 1e92, 1e93, 1e94, 1e95, 1e96, 1e97, 1e98, 1e99,
1e100, 1e101, 1e102, 1e103, 1e104, 1e105, 1e106, 1e107, 1e108, 1e109,
1e110, 1e111, 1e112, 1e113, 1e114, 1e115, 1e116, 1e117, 1e118, 1e119,
1e120, 1e121, 1e122, 1e123, 1e124, 1e125, 1e126, 1e127, 1e128, 1e129,
1e130, 1e131, 1e132, 1e133, 1e134, 1e135, 1e136, 1e137, 1e138, 1e139,
1e140, 1e141, 1e142, 1e143, 1e144, 1e145, 1e146, 1e147, 1e148, 1e149,
1e150, 1e151, 1e152, 1e153, 1e154, 1e155, 1e156, 1e157, 1e158, 1e159,
};
#undef pow10
#define npows10 ((int)(sizeof(pows10)/sizeof(pows10[0])))
#define pow10(x) fmtpow10(x)
static double
pow10(int n)
{
double d;
int neg;
neg = 0;
if(n < 0){
neg = 1;
n = -n;
}
if(n < npows10)
d = pows10[n];
else{
d = pows10[npows10-1];
for(;;){
n -= npows10 - 1;
if(n < npows10){
d *= pows10[n];
break;
}
d *= pows10[npows10 - 1];
}
}
if(neg)
return 1./d;
return d;
}
static int
xadd1(char *a, int n)
{
char *b;
int c;
if(n < 0 || n > NSIGNIF)
return 0;
for(b = a+n-1; b >= a; b--) {
c = *b + 1;
if(c <= '9') {
*b = (char)c;
return 0;
}
*b = '0';
}
a[0] = '1';
return 1;
}
static int
xsub1(char *a, int n)
{
char *b;
int c;
if(n < 0 || n > NSIGNIF)
return 0;
for(b = a+n-1; b >= a; b--) {
c = *b - 1;
if(c >= '0') {
if(c == '0' && b == a) {
*b = '9';
return 1;
}
*b = (char)c;
return 0;
}
*b = '9';
}
abort();
return 0;
}
static void
xfmtexp(char *p, int e, int ucase)
{
char se[9];
int i;
*p++ = ucase ? 'E' : 'e';
if(e < 0) {
*p++ = '-';
e = -e;
} else
*p++ = '+';
i = 0;
while(e) {
se[i++] = (char)(e % 10 + '0');
e /= 10;
}
while(i < 2)
se[i++] = '0';
while(i > 0)
*p++ = se[--i];
*p = '\0';
}
static void
xdtoa(double f, char *s, int *exp, int *neg, int *ns)
{
int d, e2, e, ee, i, ndigit;
int oerrno;
char c;
char tmp[NSIGNIF+10];
double g;
oerrno = errno;
*neg = 0;
if(f < 0) {
f = -f;
*neg = 1;
}
if(f == 0){
*exp = 0;
s[0] = '0';
s[1] = '\0';
*ns = 1;
return;
}
frexp(f, &e2);
e = (int)(e2 * .301029995664);
g = f * pow10(-e);
while(g < 1) {
e--;
g = f * pow10(-e);
}
while(g >= 10) {
e++;
g = f * pow10(-e);
}
for(i=0; i<NSIGNIF; i++) {
d = (int)g;
s[i] = (char)(d+'0');
g = (g-d) * 10;
}
s[i] = 0;
e -= NSIGNIF-1;
xfmtexp(s+NSIGNIF, e, 0);
for(i=0; i<10; i++) {
g = strtod(s, nil);
if(f > g) {
if(xadd1(s, NSIGNIF)) {
e--;
xfmtexp(s+NSIGNIF, e, 0);
}
continue;
}
if(f < g) {
if(xsub1(s, NSIGNIF)) {
e++;
xfmtexp(s+NSIGNIF, e, 0);
}
continue;
}
break;
}
for(i=NSIGNIF-1; i>=NSIGNIF-3; i--) {
c = s[i];
if(c != '9') {
s[i] = '9';
g = strtod(s, nil);
if(g != f) {
s[i] = c;
break;
}
}
}
if(s[NSIGNIF-1] == '9') {
strcpy(tmp, s);
ee = e;
if(xadd1(tmp, NSIGNIF)) {
ee--;
xfmtexp(tmp+NSIGNIF, ee, 0);
}
g = strtod(tmp, nil);
if(g == f) {
strcpy(s, tmp);
e = ee;
}
}
for(i=NSIGNIF-1; i>=NSIGNIF-3; i--) {
c = s[i];
if(c != '0') {
s[i] = '0';
g = strtod(s, nil);
if(g != f) {
s[i] = c;
break;
}
}
}
ndigit = NSIGNIF;
while(ndigit > 1 && s[ndigit-1] == '0'){
e++;
--ndigit;
}
s[ndigit] = 0;
*exp = e;
*ns = ndigit;
errno = oerrno;
}
#ifdef PLAN9PORT
static char *special[] = { "NaN", "NaN", "+Inf", "+Inf", "-Inf", "-Inf" };
#else
static char *special[] = { "nan", "NAN", "inf", "INF", "-inf", "-INF" };
#endif
int
__efgfmt(Fmt *fmt)
{
char buf[NSIGNIF+10], *dot, *digits, *p, *s, suf[10], *t;
double f;
int c, chr, dotwid, e, exp, ndigits, neg, newndigits;
int pad, point, prec, realchr, sign, sufwid, ucase, wid, z1, z2;
ulong fl;
Rune r, *rs, *rt;
if(fmt->flags&FmtLong)
f = (double)va_arg(fmt->args, long double);
else
f = va_arg(fmt->args, double);
fl = fmt->flags;
fmt->flags = 0;
prec = FDEFLT;
if(fl & FmtPrec)
prec = fmt->prec;
chr = (int)fmt->r;
ucase = 0;
switch(chr) {
case 'A':
case 'E':
case 'F':
case 'G':
chr += 'a'-'A';
ucase = 1;
break;
}
if(__isNaN(f)) {
s = special[0+ucase];
special:
fmt->flags = fl & (FmtWidth|FmtLeft);
return __fmtcpy(fmt, s, (int)strlen(s), (int)strlen(s));
}
if(__isInf(f, 1)) {
s = special[2+ucase];
goto special;
}
if(__isInf(f, -1)) {
s = special[4+ucase];
goto special;
}
digits = buf;
xdtoa(f, digits, &exp, &neg, &ndigits);
dot = fmt->decimal;
if(dot == nil)
dot = ".";
dotwid = utflen(dot);
realchr = chr;
switch(chr){
case 'g':
if(prec == 0)
prec = 1;
if(ndigits > prec) {
if(digits[prec] >= '5' && xadd1(digits, prec))
exp++;
exp += ndigits-prec;
ndigits = prec;
}
default:
case 'e':
point = 1;
z1 = 0;
e = exp + (ndigits-1);
if(realchr == 'g') {
if(-4 <= e && e < prec)
goto casef;
prec--;
}
if(1+prec >= ndigits)
z2 = 1+prec - ndigits;
else {
assert(realchr != 'g');
newndigits = 1+prec;
if(digits[newndigits] >= '5' && xadd1(digits, newndigits)) {
e++;
}
ndigits = newndigits;
z2 = 0;
}
xfmtexp(suf, e, ucase);
sufwid = (int)strlen(suf);
break;
casef:
case 'f':
if(ndigits+exp > 0) {
point = ndigits+exp;
z1 = 0;
} else {
point = 1;
z1 = 1 + -(ndigits+exp);
}
if(realchr == 'g')
prec += z1 - point;
if(point+prec >= z1+ndigits)
z2 = point+prec - (z1+ndigits);
else {
assert(realchr != 'g');
newndigits = point+prec - z1;
if(newndigits < 0) {
z1 += newndigits;
newndigits = 0;
} else if(newndigits == 0) {
if(digits[0] >= '5'){
digits[0] = '1';
newndigits = 1;
goto newdigit;
}
} else if(digits[newndigits] >= '5' && xadd1(digits, newndigits)) {
digits[newndigits++] = '0';
newdigit:
if(z1)
z1--;
else
point++;
}
z2 = 0;
ndigits = newndigits;
}
sufwid = 0;
break;
}
if(realchr == 'g' && !(fl & FmtSharp)) {
if(z1+ndigits+z2 >= point) {
if(z1+ndigits < point)
z2 = point - (z1+ndigits);
else{
z2 = 0;
while(z1+ndigits > point && digits[ndigits-1] == '0')
ndigits--;
}
}
}
wid = z1+ndigits+z2;
if(wid > point)
wid += dotwid;
else if(wid == point){
if(fl & FmtSharp)
wid += dotwid;
else
point++;
}
wid += sufwid;
sign = 0;
if(neg)
sign = '-';
else if(fl & FmtSign)
sign = '+';
else if(fl & FmtSpace)
sign = ' ';
if(sign)
wid++;
pad = 0;
if((fl & FmtWidth) && fmt->width > wid)
pad = fmt->width - wid;
if(pad && !(fl & FmtLeft) && (fl & FmtZero)){
z1 += pad;
point += pad;
pad = 0;
}
if(fmt->runes){
if(pad && !(fl & FmtLeft) && __rfmtpad(fmt, pad) < 0)
return -1;
rt = (Rune*)fmt->to;
rs = (Rune*)fmt->stop;
if(sign)
FMTRCHAR(fmt, rt, rs, sign);
while(z1>0 || ndigits>0 || z2>0) {
if(z1 > 0){
z1--;
c = '0';
}else if(ndigits > 0){
ndigits--;
c = *digits++;
}else{
z2--;
c = '0';
}
FMTRCHAR(fmt, rt, rs, c);
if(--point == 0) {
for(p = dot; *p; ){
p += chartorune(&r, p);
FMTRCHAR(fmt, rt, rs, r);
}
}
}
fmt->nfmt += (int)(rt - (Rune*)fmt->to);
fmt->to = rt;
if(sufwid && __fmtcpy(fmt, suf, sufwid, sufwid) < 0)
return -1;
if(pad && (fl & FmtLeft) && __rfmtpad(fmt, pad) < 0)
return -1;
}else{
if(pad && !(fl & FmtLeft) && __fmtpad(fmt, pad) < 0)
return -1;
t = (char*)fmt->to;
s = (char*)fmt->stop;
if(sign)
FMTCHAR(fmt, t, s, sign);
while(z1>0 || ndigits>0 || z2>0) {
if(z1 > 0){
z1--;
c = '0';
}else if(ndigits > 0){
ndigits--;
c = *digits++;
}else{
z2--;
c = '0';
}
FMTCHAR(fmt, t, s, c);
if(--point == 0)
for(p=dot; *p; p++)
FMTCHAR(fmt, t, s, *p);
}
fmt->nfmt += (int)(t - (char*)fmt->to);
fmt->to = t;
if(sufwid && __fmtcpy(fmt, suf, sufwid, sufwid) < 0)
return -1;
if(pad && (fl & FmtLeft) && __fmtpad(fmt, pad) < 0)
return -1;
}
return 0;
}