自己动手写C语言格式化输出函数(三)

   上接《自己动手写C语言格式化输出函数(二)》、《自己动手写C语言格式化输出函数(一)》。 

    八、格式化浮点数(有关浮点数的数据定义和底层的数据转换函数见自己动手写C语言浮点数转换字符串函数》一文)。

// 转换浮点数信息到浮点数记录fRec。参数:格式记录,格式方式标记,浮点数记录
static void GetFloatRec(FormatRec *rec, INT flag, FloatRec *fRec)
{
	EXTENDED value;
	if (rec->precision < 0)
		rec->precision = F_DEFDECIMALS;
	else if (rec->precision > F_MAXDECIMALS)
		rec->precision = F_MAXDECIMALS;
	if (rec->type == TYPE_LLONG)
	{
		value = *(PEXTENDED)rec->param;
		rec->param += TS_EXTENDED;
	}
	else
	{
		value = *(double*)rec->param;
		rec->param += TS_DOUBLE;
	}
	switch (flag)
	{
		case 0:		// %f
			FloatResolve(&value, F_MAXPRECISION, rec->precision, fRec);
			break;
		case 1:		// %e or %E
			FloatResolve(&value, rec->precision + 1, 9999, fRec);
			break;
		case 2:		// %g or %G
			FloatResolve(&value, rec->precision, 9999, fRec);
	}
	if (fRec->negative)
		rec->negative = -1;
}

// 格式化小数字串。参数:缓冲区,格式记录,数字串,数字串长度。返回缓冲区尾偏移
static LPSTR FormatDecimalA(LPSTR buffer, FormatRec *rec, LPCSTR str, INT strLen)
{
	LPSTR p = buffer;
	INT spaces = rec->width - strLen;
	if (rec->negative)
	{
		spaces --;
		if (rec->left || rec->zero)
			*p ++ = (rec->negative == -1? CHAR_NEG : CHAR_POS);
	}
	if (rec->left == FALSE)
	{
		if (spaces > 0)
		{
			memset(p, rec->zero? CHAR_ZERO : CHAR_SPACE, spaces);
			p += spaces;
		}
		if (rec->negative && !rec->zero)
			*p ++ = (rec->negative == -1? CHAR_NEG : CHAR_POS);
	}
	memcpy(p, str, strLen);
	p += strLen;
	if (rec->left && spaces > 0)
	{
		memset(p, CHAR_SPACE, spaces);
		p += spaces;
	}
	return p;
}

#define	F_MAXEXPONENT	45
#define	F_MINEXPONENT	-45

// 输出指数字符串到buffer,返回指数字符串长度
INT PutExponent(LPSTR buffer, CONST FloatRec *rec)
{
	LPSTR p = buffer;
	INT e, exp = rec->digits[0]? rec->exponent - 1 : 0;
	*p ++ = rec->negative & 0x80? 'E' : 'e';
	if (exp < 0)
	{
		exp = -exp;
		*p ++ = '-';
	}
	else *p ++ = '+';
	if ((e = (exp / 1000)) != 0)
	{
		*p ++ = e + 0x30;
		exp %= 1000;
	}
	*p ++ = exp / 100 + 0x30;
	exp %= 100;
	*(PUSHORT)p = (((exp % 10) << 8) | (exp / 10)) + 0x3030;
	return (INT)(p - buffer + 2);
}

// 按浮点数记录信息转换为指数格式数字串。
// 参数:缓冲区,浮点数记录,转换精度,是否强制小数位
static INT FloatExponentA(LPSTR buffer, CONST FloatRec *rec, INT precision, BOOL decPoint)
{
	LPSTR p = buffer;
	LPCSTR digits = rec->digits;
	if (*digits)
		*p ++ = *digits ++;
	else
		*p ++ = '0';
	if (precision > 0 || decPoint)
	{
		for (*p ++ = '.'; precision > 0 && *digits; *p ++ = *digits ++, precision --);
		for (; precision > 0; *p ++ = '0', precision --);
	}
	p += PutExponent(p, rec);
	return (INT)(p - buffer);
}

// 按浮点数记录信息转换为小数格式数字串。
// 参数:缓冲区,浮点数记录,转换精度,是否强制小数位
static INT FloatDecimalA(LPSTR buffer, CONST FloatRec *rec, INT precision, BOOL decPoint)
{
	LPSTR p;
	LPCSTR digits;
	INT exp = rec->exponent;
	if (exp > F_MAXEXPONENT || exp < F_MINEXPONENT)
		return FloatExponentA(buffer, rec, precision, decPoint);
	p = buffer;
	digits = rec->digits;
	if (exp > 0)
	{
		for (; exp > 0 && *digits; *p ++ = *digits ++, exp --);
		for (; exp > 0; *p ++ = '0', exp --);
		if (decPoint || precision > 0)
			*p ++ = '.';
	}
	else
	{
		exp = -exp;
		precision -= exp;
		if (precision < 0)
		{
			exp += precision;
			precision = 0;
		}
		*p ++ = '0';
		if (exp > 0 || decPoint || precision > 0)
		{
			*p ++ = '.';
			for (; exp > 0; *p ++ = '0', exp --);
		}
	}
	for (; precision > 0 && *digits; *p ++ = *digits ++, precision --);
	for (; precision > 0; *p ++ = '0', precision --);
	return (INT)(p - buffer);
}

// 浮点数格式化为小数串。参数:缓冲区,格式记录。返回缓冲区尾偏移
static LPSTR FormatFloatFA(LPSTR buffer, FormatRec *rec)
{
	FloatRec fRec;
	INT len;
	CHAR tmp[F_MAXDECIMALS+48];

	GetFloatRec(rec, 0, &fRec);
	if (fRec.digits[0] > '9')		// nan or inf
		return FormatDecimalA(buffer, rec, fRec.digits, 3);
	len = FloatDecimalA(tmp, &fRec, rec->precision, rec->decimals);
	return FormatDecimalA(buffer, rec, tmp, len);
}

// 浮点数格式化为指数串。参数:缓冲区,格式记录。返回缓冲区尾偏移
static LPSTR FormatFloatEA(LPSTR buffer, FormatRec *rec, CHAR expChar)
{
	FloatRec fRec;
	INT len;
	CHAR tmp[F_MAXDECIMALS+8];

	GetFloatRec(rec, 1, &fRec);
	if (fRec.digits[0] > '9')		// nan or inf
		return FormatDecimalA(buffer, rec, fRec.digits, 3);
	if (expChar == 'E')
		fRec.negative |= 0x80;	// 高位置1,大写
	len = FloatExponentA(tmp, &fRec, rec->precision, rec->decimals);
	return FormatDecimalA(buffer, rec, tmp, len);
}

// 浮点数格式化为小数串或者指数串。参数:缓冲区,格式记录。返回缓冲区尾偏移
static LPSTR FormatFloatGA(LPSTR buffer, FormatRec *rec, CHAR expChar)
{
	FloatRec fRec;
	INT len, precision;
	CHAR tmp[F_MAXDECIMALS+48];

	GetFloatRec(rec, 2, &fRec);
	if (fRec.digits[0] > '9')	// nan or inf
		return FormatDecimalA(buffer, rec, fRec.digits, 3);
	if (expChar == 'G')
		fRec.negative |= 0x80;	// 高位置1,大写
	if (fRec.exponent > rec->precision || fRec.exponent < -3)
	{
		precision = rec->decimals? rec->precision - 1 : lstrlenA(fRec.digits) - 1;
		len = FloatExponentA(tmp, &fRec, precision, rec->decimals);
	}
	else
	{
		precision = rec->decimals? rec->precision - fRec.exponent : lstrlenA(fRec.digits) - fRec.exponent;
		if (precision < 0) precision = 0;
		len = FloatDecimalA(tmp, &fRec, precision, rec->decimals);
	}

	return FormatDecimalA(buffer, rec, tmp, len);
}

    在sprintfA函数中,浮点数的格式化是最复杂的。浮点数有2种表现形式,即小数形式和指数形式,分别用"%f"和"%e"格式表示,另外还有个"%g"格式,这是个自动格式,即sprintfA通过分析后,自行决定采用哪种形式。

    在以小数形式的格式化中,对数据的格式化有个极限长度,不然,在扩展精度浮点数下,有些浮点数长度可达到近5000位,即使是双精度浮点数,最高长度也达300多。在printf系列函数中,这个极限长度随编译器不同而不同,有的将这个值定为100,有的定为单精度浮点数的最大表现形式,即38等,我在这里把它定为了正负45位,当数据超过这个极限长度,就自动采用指数形式来格式化数据了。

    在介绍sprintfA数据定义时就说过,由于sprintfA的可变参数部分没有参数原型供编译器对照,所以在输入浮点数参数时要注意与格式字符串中对应的浮点数精度匹配,32位编译器的浮点数缺省精度是64位双精度数,即使你给的参数变量是个单精度数,也会扩展为双精度数,如果参数变量是long double,而你使用的编译器支持80位扩展精度浮点数时,传递的是80位扩展精度数,否则也是双精度数,如果你给出一个整数,编译器是不会自动转换的。如果你在参数位置输入的是常数就更应该注意了,123,123L,123.0f,123.0,123.0L这几种常数形式是不同的(L也可是小写),分别是整数,长整数,单精度浮点数,双精度浮点数,扩展精度浮点数(如果编译器不支持,也是双精度数)。所以,在32位及以上编译器中格式%f和%lf是等同的,自然,在不支持扩展精度浮点数的编译器中,%llf(%Lf)也等同于%f。

    本文格式化浮点数时用到的FloatResolve函数以及有关数据定义见自己动手写C语言浮点数转换字符串函数

    有关sprintfA函数的介绍就全部完毕。文章代码没进行严格的测试。

 

    声明:本文代码主要供学习使用,如作其它用途,出问题慨不负责。

    水平有限,错误在所难免,欢迎指正和指导。邮箱地址:maozefa@hotmail.com

 


  • 15
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 17
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值