自己动手写C语言float浮点数转换字符串的函数

最近在项目中用到了holtek厂商的HT45F24A和BA45F5650两款单片机。
用的开发工具是HT-IDE3000,烧录软件是HOPE3000。
这两款单片机都是8位的单片机,支持寄存器位操作。
HT45F24A单片机不带UART串口,要想实现串口功能,只能自己用定时器操作GPIO来模拟UART发送时序。
BA45F5650单片机带UART串口,可直接使用。
IDE里面支持float和double浮点类型,但是却没有头文件,不支持printf函数,sprintf函数,编译器不支持stdarg可变参数函数,所以也没法自己实现printf函数。
IDE里面有字符串转浮点数的函数,却没有浮点数转字符串的函数,这个功能得自己编程实现。
另外,IDE里面的float是3/4精度浮点数(3字节),double是单精度浮点数(4字节)。
而电脑上的float是单精度浮点数(4字节),double是双精度浮点数(8字节)。

自己动手写C语言float浮点数转换字符串的函数_第1张图片

程序下载链接:https://pan.baidu.com/s/1SLjIJdzTKRk06zur2QshhQ?pwd=m8ay

先在电脑上用Visual Studio 2010实现4字节单精度浮点数转字符串的功能。
双精度浮点数double和四精度浮点数long double转字符串已经有人写出来了:
https://www.cnblogs.com/carekee/articles/3124256.html
只需要参照他的思想,写一个单精度浮点数(电脑上的float,51/合泰单片机上的double)的转换函数就可以了。

电脑浮点数格式
总长度 符号位 指数位 尾数位
half float 2字节 1位 5位 11位
float 4字节 1位 8位 23位
double 8字节 1位 11位 52位
long double 16字节 1位 15位 64或112位
合泰单片机浮点数格式
总长度 符号位 指数位 尾数位
float 3字节 1位 8位 15位
double 4字节 1位 8位 23位

【ftoa.h】

#pragma once

#define FTOA_PRECISION 8 // 转换时保留的有效数字位数
#define FTOA_PRECISION_MAX 1e8 // 必须等于10^FTOA_PRECISION
typedef uint32_t ftoa_fixnum_t; // 此类型至少要能保存10*FTOA_PRECISION_MAX那么大的数

#define FLOAT_VAL(var) (*(float *)&(var))
#define FLOAT_HEXVAL(var) (*((uint32_t *)&(var)))
#define FLOAT_SIGN(var) (FLOAT_HEXVAL(var) >> 31) // 1bit
#define FLOAT_CLEARSIGN(var) (FLOAT_HEXVAL(var) & 0x7fffffff)
#define FLOAT_EXPONENT(var) (((FLOAT_HEXVAL(var) >> 23) & 0xff) - 127) // 8bits
#define FLOAT_MANTISSA(var) (FLOAT_HEXVAL(var) & 0x7fffff) // 23bits
#define MAKE_FLOAT_HEXVAL(s, e, m) ((((uint32_t)(s) & 1) << 31) | (((uint32_t)((e) + 127) & 0xff) << 23) | ((uint32_t)(m) & 0x7fffff))
#define MAKE_FLOAT(f, s, e, m) FLOAT_HEXVAL(f) = MAKE_FLOAT_HEXVAL((s), (e), (m))

char *ftoa(float value);
char *ltoa(long value);

【ftoa.c】

#include 
#include 
#include "ftoa.h"

static const float float_table[31] = {
	1.0e1f, 1.0e2f, 1.0e3f, 1.0e4f, 1.0e5f, 1.0e6f, 1.0e7f, 1.0e8f, 
	1.0e9f, 1.0e10f, 1.0e11f, 1.0e12f, 1.0e13f, 1.0e14f, 1.0e15f, 1.0e16f, 
	1.0e17f, 1.0e18f, 1.0e19f, 1.0e20f, 1.0e21f, 1.0e22f, 1.0e23f, 1.0e24f, 
	1.0e25f, 1.0e26f, 1.0e27f, 1.0e28f, 1.0e29f, 1.0e30f, 1.0e31f
};
static const float float_table_2[1] = {1.0e32f};

char *ftoa(float value)
{
	static char str[50];
	char *p = str;
	float num;
	ftoa_fixnum_t fixnum;
	int16_t e, k, a, b, i;
	uint8_t negative = FLOAT_SIGN(value);

	/* 总体思想 */
	// (1) 如果浮点数的整数部分能够放进一个整型变量里面, 直接把这个整型变量的值转换成十进制字符串就行了
	// (2) 如果浮点数的整数部分太大, 超过了整型变量的取值范围, 则除以10^k, 就能成功放进整型变量里面了, 然后把整型变量转换成十进制字符串, 字符串末尾要添0
	// (3) 如果浮点数的整数部分为0, 只有小数部分, 那么乘以10^(-k), 整数部分就不是0了, 放进整型变量后就可以转换成字符串了, 再在合适的位置点上小数点
	// 为了使结果包含尽可能多的有效数字, 应该正确选择k值, 使整数部分能够尽可能占满整型变量的最大存储空间
	// 变量e决定了最后在字符串的什么位置点上小数点

	/* 第一步: 先把|value|改写成num*(10^k)的形式 */
	// |value|=num*(10^k), 其中k=32*a+b
	// num的整数部分的大小有如下两个要求:
	// (1) 必须要在ftoa_fixnum_t类型能表示的数值范围内, 并且不能为负数
	// (2) 数位尽可能多, 最好是有FTOA_PRECISION位数

	// 将二进制指数转化成十进制指数
	// 设2^x=10^e, 则e=lg(2^x)=xlg2 (x就是浮点数里面那个二进制指数)
	// lg2是一个无理数, 可以取近似值19728/65536
	e = FLOAT_EXPONENT(value);
	if (e == -127)
	{
		if (!negative)
			strcpy(str, "0");
		else
			strcpy(str, "-0");
		return str;
	}
	else if (e == 128)
	{
		if (FLOAT_MANTISSA(value) == 0)
		{
			if (!negative)
				strcpy(str, "inf");
			else
				strcpy(str, "-inf");
		}
		else
		{
			if (!negative)
				strcpy(str, "nan");
			else
				strcpy(str, "-nan");
		}
		return str;
	}
	e = ((int32_t)e * 19728) / 65536; // 由于lg2的值不精确, 所以这里的e值是近似值, 但是没有关系, 只要整型变量fixnum能放得下num, 就不会转换出错
	// 根据e的值确定k值, 使num的数位尽可能多
	k = -(FTOA_PRECISION - (e + 1)); // e是科学计数法的指数, 那e+1就是位数, 例如10^4是5位数
	// 再根据k值和value值确定num值
	// k/32=a......b
	// num=|value|/(10^k)
	//    =|value|/[10^(32*a+b)]
	//    =|value|/[10^(32*a)]/(10^b)
	FLOAT_HEXVAL(num) = FLOAT_CLEARSIGN(value); // 取浮点数绝对值
	if (k > 0)
	{
		a = k / 32;
		b = k % 32;
		if (a != 0)
			num /= float_table_2[a - 1];
		if (b != 0)
			num /= float_table[b - 1];
	}
	else if (k < 0)
	{
		a = (-k) / 32;
		b = (-k) % 32;
		if (a != 0)
			num *= float_table_2[a - 1];
		if (b != 0)
			num *= float_table[b - 1];
	}
	// num的整数部分四舍五入
	// 0.4+0.5=0.9=>0, 0.5+0.5=1.0=>1, 0.6+0.5=1.1=>1
	num += 0.5f;

	// 如果num的位数=FTOA_PRECISION_MAX, 那么num的位数就不是<=FTOA_PRECISION, 而是=FTOA_PRECISION+1
	// 位数多了一位, 说明前面的e值太小了, 导致k的大小不合适
	if (num >= FTOA_PRECISION_MAX)
	{
		// e的值增加1, 然后k的值也增加1
		e++;
		k++;
		// 为了使等式|value|=num*(10^k)仍然成立, num的值要缩小10倍才行
		num /= 10;
	}
	// num是由|value|/(10^k)得来的, 理论上无论k取几, num的值都是value的十进制小数位
	// 比如num=12345678901234567890, 当k=15时, num=12345; 当k=12时, num=12345678
	// 就算是前面e的值因lg2不精确没算准确, 也不可能出现num不是value的十进制小数位的情况, 搞清楚这一点非常重要
	// k的值又是由e的值得来的, 所以小数点是不可能点错位置的
	// 只不过k的大小如果不合适, 那么num的大小就会太大或者太小, 没法存到fixnum整型变量里面, 就没法转成字符串

	/* 第二步: 取num的整数部分, 存到fixnum整型变量中, 舍弃小数部分 */
#if 0
	// (方法1)
	fixnum = (ftoa_fixnum_t)num;
#else
	// (方法2)
	// 请注意在浮点数标准格式里面, 尾数是有小数点的, 小数点在尾数的最左侧, 小数点左边还有一个隐藏的1
	fixnum = FLOAT_MANTISSA(num) | (1ul << 23); // 取尾数, 补上小数点前隐藏的1, 然后去掉小数点
	i = FLOAT_EXPONENT(num) - 23; // 取指数 (因为上一步去掉了小数点, 导致原数扩大了2^23倍, 所以取出来的指数要减23)
	// 乘上2的(指数-23)次方
	if (i > 0)
		fixnum <<= i; 
	else if (i < 0)
		fixnum >>= -i;
#endif

	/* 第三步: 根据e值和fixnum值输出字符串 */
	// 小数点的位置应该点在fixnum数值从左边数第e+1位数的后面
	if (negative)
		*p++ = '-';
	if (e + 1 < 1)
	{
		// 如果e + 1 < 1, 就要在前面添0
		*p++ = '0';
		*p++ = '.';
		while (e + 1 < 0)
		{
			*p++ = '0';
			e++;
		}
		// 后面只有数字, 不再添加小数点
		i = FTOA_PRECISION;
	}
	else if (e + 1 < FTOA_PRECISION)
	{
		// 小数点在数字中间
		i = FTOA_PRECISION + 1;
	}
	else
	{
		// 没有小数点, 数字末尾可能要补0
		i = e + 1;
	}
	p[i--] = '\0';
	while (i >= 0)
	{
		if (i >= FTOA_PRECISION && e + 1 >= FTOA_PRECISION)
			p[i--] = '0';
		else
		{
			if (i == e + 1 && i != 0)
				p[i--] = '.';
			p[i--] = '0' + fixnum % 10;
			fixnum /= 10;
		}
	}

	/* 第四步: 去掉小数点末尾多余的0 */
	if (e + 1 < FTOA_PRECISION)
	{
		i = FTOA_PRECISION;
		if (p[i] == '\0')
			i--;
		while (i >= 0 && p[i] == '0')
		{
			p[i] = '\0';
			i--;
		}
		if (i >= 0 && p[i] == '.')
			p[i] = '\0';
	}
	return str;
}

char *ltoa(long value)
{
	static char str[12];
	char temp;
	uint8_t i, n, start = 0;
	
	if (value == 0)
	{
		str[0] = '0';
		str[1] = '\0';
	}
	else
	{
		// 正数的最大值是2147483647
		// 负数的绝对值的最大值是2147483648
		// 负数的表示范围更大, 所以把所有的正数都转换成负数
		if (value < 0)
		{
			str[0] = '-';
			start = 1;
		}
		else
			value = -value;
		
		// 从左到右依次存放低位到高位
		for (i = start; value != 0; i++)
		{
			str[i] = '0' + (-(value % 10));
			value /= 10;
		}
		str[i] = '\0';
		
		// 字符串倒序: 高位移到左边, 低位移到右边
		n = i - start;
		for (i = 0; i < n / 2; i++)
		{
			temp = str[start + i];
			str[start + i] = str[start + n - i - 1];
			str[start + n - i - 1] = temp;
		}
	}
	return str;
}

【main.c】

#include 
#include 
#include "ftoa.h"

int main(void)
{
	float f;

	MAKE_FLOAT(f, 0, 13, 7 << 20);
	printf("1.875*(2^13)=%s\n", ftoa(f));
	printf("%s\n", ftoa(-0.00075f));
	printf("%s\n", ftoa(-3.1415926f));
	printf("%s\n", ftoa(-13.1415926f));
	printf("%s\n", ftoa(0.0012345678f));
	printf("%s\n", ftoa(0.012345678f));
	printf("%s\n", ftoa(0.12345678f));
	printf("%s\n", ftoa(1.2345678f));
	printf("%s\n", ftoa(12.345678f));
	printf("%s\n", ftoa(123.45678f));
	printf("%s\n", ftoa(1234.5678f));
	printf("%s\n", ftoa(12345.678f));
	printf("%s\n", ftoa(123456.78f));
	printf("%s\n", ftoa(1234567.8f));
	printf("%s\n", ftoa(12345678.0f));
	printf("%s\n", ftoa(123456789.0f));
	printf("%s\n", ftoa(123456789012.0f));
	printf("%s\n", ftoa(1.23e38f));
	printf("%s\n", ftoa(-1.23e-38f));
	
	MAKE_FLOAT(f, 0, -127, 0);
	printf("%f %s\n", f, ftoa(f));
	MAKE_FLOAT(f, 0, -127, 7 << 20);
	printf("%f %s\n", f, ftoa(f));
	MAKE_FLOAT(f, 1, -127, 0);
	printf("%f %s\n", f, ftoa(f));
	MAKE_FLOAT(f, 1, -127, 7 << 20);
	printf("%f %s\n", f, ftoa(f));
	MAKE_FLOAT(f, 0, 128, 0);
	printf("%f %s\n", f, ftoa(f));
	MAKE_FLOAT(f, 0, 128, 7 << 20);
	printf("%f %s\n", f, ftoa(f));
	MAKE_FLOAT(f, 1, 128, 0);
	printf("%f %s\n", f, ftoa(f));
	MAKE_FLOAT(f, 1, 128, 7 << 20);
	printf("%f %s\n", f, ftoa(f));

	printf("%s ", ltoa(0));
	printf("%s ", ltoa(123450));
	printf("%s\n", ltoa(-67890));
	printf("%s ", ltoa(2147483647));
	printf("%s ", ltoa(-2147483647));
	printf("%s\n", ltoa(-2147483647 - 1));

	return 0;
}

程序运行结果:

自己动手写C语言float浮点数转换字符串的函数_第2张图片

然后再移植到BA45F5650单片机上运行。
【strconv.h】

#define FTOA_PRECISION 8
#define FTOA_PRECISION_MAX 1e8
typedef uint32_t ftoa_fixnum_t;

#define FLOAT_SIGN_HEXVAL(hexval) (((hexval) >> 23) & 1) // 1bit
#define FLOAT_CLEARSIGN_HEXVAL(hexval) ((hexval) & 0x7fffff)
#define FLOAT_EXPONENT_HEXVAL(hexval) ((((hexval) >> 15) & 0xff) - 127) // 8bits
#define FLOAT_MANTISSA_HEXVAL(hexval) ((hexval) & 0x7fff) // 15bits
#define MAKE_FLOAT_HEXVAL(s, e, m) ((((uint32_t)(s) & 1) << 23) | (((uint32_t)((e) + 127) & 0xff) << 15) | ((uint32_t)(m) & 0x7fff))
#define MAKE_FLOAT(f, s, e, m) \
		do { \
			uint32_t temp = MAKE_FLOAT_HEXVAL((s), (e), (m)); \
			memcpy(&(f), &temp, sizeof(f)); \
		} while (0)

#define DTOA_PRECISION 8
#define DTOA_PRECISION_MAX 1e8
typedef uint32_t dtoa_fixnum_t;

#define DOUBLE_SIGN_HEXVAL(hexval) ((hexval) >> 31) // 1bit
#define DOUBLE_CLEARSIGN_HEXVAL(hexval) ((hexval) & 0x7fffffff)
#define DOUBLE_EXPONENT_HEXVAL(hexval) ((((hexval) >> 23) & 0xff) - 127) // 8bits
#define DOUBLE_MANTISSA_HEXVAL(hexval) ((hexval) & 0x7fffff) // 23bits
#define MAKE_DOUBLE_HEXVAL(s, e, m) ((((uint32_t)(s) & 1) << 31) | (((uint32_t)((e) + 127) & 0xff) << 23) | ((uint32_t)(m) & 0x7fffff))
#define MAKE_DOUBLE(d, s, e, m) \
		do { \
			uint32_t temp = MAKE_DOUBLE_HEXVAL((s), (e), (m)); \
			memcpy(&(d), &temp, sizeof(d)); \
		} while (0)

char *ftoa(float value);
char *dtoa(double value);
char *ltoa(long value);
char *hexstr(uint32_t value, uint8_t width);

【strconv.c】

#include 
#include 
#include "strconv.h"

static const float float_table[31] = {
	1.0e1f, 1.0e2f, 1.0e3f, 1.0e4f, 1.0e5f, 1.0e6f, 1.0e7f, 1.0e8f, 
	1.0e9f, 1.0e10f, 1.0e11f, 1.0e12f, 1.0e13f, 1.0e14f, 1.0e15f, 1.0e16f, 
	1.0e17f, 1.0e18f, 1.0e19f, 1.0e20f, 1.0e21f, 1.0e22f, 1.0e23f, 1.0e24f, 
	1.0e25f, 1.0e26f, 1.0e27f, 1.0e28f, 1.0e29f, 1.0e30f, 1.0e31f
};
static const float float_table_2[1] = {1.0e32f};
static const double double_table[31] = {
	1.0e1, 1.0e2, 1.0e3, 1.0e4, 1.0e5, 1.0e6, 1.0e7, 1.0e8, 
	1.0e9, 1.0e10, 1.0e11, 1.0e12, 1.0e13, 1.0e14, 1.0e15, 1.0e16, 
	1.0e17, 1.0e18, 1.0e19, 1.0e20, 1.0e21, 1.0e22, 1.0e23, 1.0e24, 
	1.0e25, 1.0e26, 1.0e27, 1.0e28, 1.0e29, 1.0e30, 1.0e31
};
static const double double_table_2[1] = {1.0e32};
static char strconv_str[50];

char *ftoa(float value)
{
	char *p = strconv_str;
	float num;
	ftoa_fixnum_t fixnum;
	int16_t e, k, a, b, i;
	uint8_t negative;
	uint32_t num_hexval, value_hexval;

	memcpy(&value_hexval, &value, sizeof(value_hexval));
	negative = FLOAT_SIGN_HEXVAL(value_hexval);

	e = FLOAT_EXPONENT_HEXVAL(value_hexval);
	if (e == -127)
	{
		if (!negative)
			strcpy(strconv_str, "0");
		else
			strcpy(strconv_str, "-0");
		return strconv_str;
	}
	else if (e == 128)
	{
		if (FLOAT_MANTISSA_HEXVAL(value_hexval) == 0)
		{
			if (!negative)
				strcpy(strconv_str, "inf");
			else
				strcpy(strconv_str, "-inf");
		}
		else
		{
			if (!negative)
				strcpy(strconv_str, "nan");
			else
				strcpy(strconv_str, "-nan");
		}
		return strconv_str;
	}
	e = ((int32_t)e * 19728) / 65536;
	k = -(FTOA_PRECISION - (e + 1));
	num_hexval = FLOAT_CLEARSIGN_HEXVAL(value_hexval);
	memcpy(&num, &num_hexval, sizeof(num));
	if (k > 0)
	{
		a = k / 32;
		b = k % 32;
		if (a != 0)
			num /= float_table_2[a - 1];
		if (b != 0)
			num /= float_table[b - 1];
	}
	else if (k < 0)
	{
		a = (-k) / 32;
		b = (-k) % 32;
		if (a != 0)
			num *= float_table_2[a - 1];
		if (b != 0)
			num *= float_table[b - 1];
	}
	num += 0.5f;

	if (num >= FTOA_PRECISION_MAX)
	{
		e++;
		k++;
		num /= 10;
	}
	
	memcpy(&num_hexval, &num, sizeof(num_hexval));
	fixnum = FLOAT_MANTISSA_HEXVAL(num_hexval) | (1ul << 15);
	i = FLOAT_EXPONENT_HEXVAL(num_hexval) - 15;
	if (i > 0)
		fixnum <<= i; 
	else if (i < 0)
		fixnum >>= -i;
		
	if (negative)
		*p++ = '-';
	if (e + 1 < 1)
	{
		*p++ = '0';
		*p++ = '.';
		while (e + 1 < 0)
		{
			*p++ = '0';
			e++;
		}
		i = FTOA_PRECISION;
	}
	else if (e + 1 < FTOA_PRECISION)
		i = FTOA_PRECISION + 1;
	else
		i = e + 1;
	p[i--] = '\0';
	while (i >= 0)
	{
		if (i >= FTOA_PRECISION && e + 1 >= FTOA_PRECISION)
			p[i--] = '0';
		else
		{
			if (i == e + 1 && i != 0)
				p[i--] = '.';
			p[i--] = '0' + fixnum % 10;
			fixnum /= 10;
		}
	}

	if (e + 1 < FTOA_PRECISION)
	{
		i = FTOA_PRECISION;
		if (p[i] == '\0')
			i--;
		while (i >= 0 && p[i] == '0')
		{
			p[i] = '\0';
			i--;
		}
		if (i >= 0 && p[i] == '.')
			p[i] = '\0';
	}
	return strconv_str;
}

char *dtoa(double value)
{
	char *p = strconv_str;
	double num;
	dtoa_fixnum_t fixnum;
	int16_t e, k, a, b, i;
	uint8_t negative;
	uint32_t num_hexval, value_hexval;

	memcpy(&value_hexval, &value, sizeof(value_hexval));
	negative = DOUBLE_SIGN_HEXVAL(value_hexval);

	e = DOUBLE_EXPONENT_HEXVAL(value_hexval);
	if (e == -127)
	{
		if (!negative)
			strcpy(strconv_str, "0");
		else
			strcpy(strconv_str, "-0");
		return strconv_str;
	}
	else if (e == 128)
	{
		if (DOUBLE_MANTISSA_HEXVAL(value_hexval) == 0)
		{
			if (!negative)
				strcpy(strconv_str, "inf");
			else
				strcpy(strconv_str, "-inf");
		}
		else
		{
			if (!negative)
				strcpy(strconv_str, "nan");
			else
				strcpy(strconv_str, "-nan");
		}
		return strconv_str;
	}
	e = ((int32_t)e * 19728) / 65536;
	k = -(DTOA_PRECISION - (e + 1));
	num_hexval = DOUBLE_CLEARSIGN_HEXVAL(value_hexval);
	memcpy(&num, &num_hexval, sizeof(num));
	if (k > 0)
	{
		a = k / 32;
		b = k % 32;
		if (a != 0)
			num /= double_table_2[a - 1];
		if (b != 0)
			num /= double_table[b - 1];
	}
	else if (k < 0)
	{
		a = (-k) / 32;
		b = (-k) % 32;
		if (a != 0)
			num *= double_table_2[a - 1];
		if (b != 0)
			num *= double_table[b - 1];
	}
	num += 0.5;

	if (num >= DTOA_PRECISION_MAX)
	{
		e++;
		k++;
		num /= 10;
	}
	
	memcpy(&num_hexval, &num, sizeof(num_hexval));
	fixnum = DOUBLE_MANTISSA_HEXVAL(num_hexval) | (1ul << 23);
	i = DOUBLE_EXPONENT_HEXVAL(num_hexval) - 23;
	if (i > 0)
		fixnum <<= i; 
	else if (i < 0)
		fixnum >>= -i;
		
	if (negative)
		*p++ = '-';
	if (e + 1 < 1)
	{
		*p++ = '0';
		*p++ = '.';
		while (e + 1 < 0)
		{
			*p++ = '0';
			e++;
		}
		i = DTOA_PRECISION;
	}
	else if (e + 1 < DTOA_PRECISION)
		i = DTOA_PRECISION + 1;
	else
		i = e + 1;
	p[i--] = '\0';
	while (i >= 0)
	{
		if (i >= DTOA_PRECISION && e + 1 >= DTOA_PRECISION)
			p[i--] = '0';
		else
		{
			if (i == e + 1 && i != 0)
				p[i--] = '.';
			p[i--] = '0' + fixnum % 10;
			fixnum /= 10;
		}
	}

	if (e + 1 < DTOA_PRECISION)
	{
		i = DTOA_PRECISION;
		if (p[i] == '\0')
			i--;
		while (i >= 0 && p[i] == '0')
		{
			p[i] = '\0';
			i--;
		}
		if (i >= 0 && p[i] == '.')
			p[i] = '\0';
	}
	return strconv_str;
}

char *ltoa(long value)
{
	char temp;
	uint8_t i, n, start = 0;
	
	if (value == 0)
	{
		strconv_str[0] = '0';
		strconv_str[1] = '\0';
	}
	else
	{
		if (value < 0)
		{
			strconv_str[0] = '-';
			start = 1;
		}
		else
			value = -value;
		
		for (i = start; value != 0; i++)
		{
			strconv_str[i] = '0' + (-(value % 10));
			value /= 10;
		}
		strconv_str[i] = '\0';
		
		n = i - start;
		for (i = 0; i < n / 2; i++)
		{
			temp = strconv_str[start + i];
			strconv_str[start + i] = strconv_str[start + n - i - 1];
			strconv_str[start + n - i - 1] = temp;
		}
	}
	return strconv_str;
}

char *hexstr(uint32_t value, uint8_t width)
{
	char ch;
	uint8_t i, j = 2;
	uint8_t ignore = 1;
	
	strconv_str[0] = '0';
	strconv_str[1] = 'x';
	for (i = 0; i < 8; i++)
	{
		ch = (value >> 28) & 0xff;
		value <<= 4;
		if (ignore && ch == 0 && 8 - i > width)
		{
			continue;
		}
		
		ignore = 0;
		if (ch >= 0 && ch <= 9)
		{
			ch += '0';
		}
		else if (ch >= 10 && ch <= 15)
		{
			ch += 'a' - 10;
		}
		strconv_str[j] = ch;
		j++;
	}
	strconv_str[j] = '\0';
	return strconv_str;
}

【uart.h】

void dump_data(const void *data, int len);
void uart_init();
void uart_send(uint8_t data);
void uart_send_char(char ch);
void uart_send_string(const char *s);
void uart_wait_tx();

【uart.c】

#include 
#include 
#include "uart.h"

void dump_data(const void *data, int len)
{
	const char *list = "0123456789ABCDEF";
	const uint8_t *p = data;
	
	while (len--)
	{
		uart_send_char(list[*p >> 4]);
		uart_send_char(list[*p & 15]);
		p++;
	}
	uart_send_char('\n');
}

void uart_init()
{
	// select PA6 as UART_RX
	_pas15 = 1;
	_pas14 = 1;
	_ifs11 = 1;
	_ifs10 = 0;
	_papu6 = 1; // pull-up
	// select PA3 as UART_TX
	_pas07 = 1;
	_pas06 = 0;
	
	// change HICR frequency from 2MHz to 8MHz
	_hirc1 = 1;
	_hirc0 = 0;
	while (!_hircf);
	
	// set baudrate to 38400
	// 2MHz/(16*(12+1))->9615.385
	// 8MHz/(16*(12+1))->38461.538
	_brgh = 1;
	_brg = 12;
	
	// enable UART
	_uarten = 1;
	_rxen = 1;
	_txen = 1;
}

void uart_send(uint8_t data)
{
	while (!_txif);
	_txr_rxr = data;
}

void uart_send_char(char ch)
{
	if (ch == '\n')
		uart_send('\r');
	uart_send(ch);
}

void uart_send_string(const char *s)
{
	while (*s)
		uart_send_char(*s++);
}

void uart_wait_tx()
{
	while (!_tidle);
}

【main.c】

#include 
#include 
#include 
#include 
#include "strconv.h"
#include "uart.h"

static void test_float()
{
	float value;
	
	uart_send_string(ftoa(1.245f));
	uart_send_string(" ");
	uart_send_string(ftoa(-9.36f));
	uart_send_string(" ");
	uart_send_string(ftoa(1.78f));
	uart_send_string(" ");
	uart_send_string(ftoa(1456.0f));
	uart_send_string(" ");
	uart_send_string(ftoa(0.00075f));
	uart_send_string(" ");
	uart_send_string(ftoa(0.0000491f));
	uart_send_string("\n");
	
	uart_send_string(ftoa(2.57e14f));
	uart_send_string(" ");
	uart_send_string(ftoa(9.46e37f));
	uart_send_string("\n");
	
	uart_send_string(ftoa(0.0f));
	uart_send_string(" ");
	uart_send_string(ftoa(-0.0f));
	uart_send_string(" ");
	MAKE_FLOAT(value, 0, -127, 0);
	uart_send_string(ftoa(value));
	uart_send_string(" ");
	MAKE_FLOAT(value, 1, -127, 0);
	uart_send_string(ftoa(value));
	uart_send_string(" ");
	MAKE_FLOAT(value, 0, -127, 7ul << 12);
	uart_send_string(ftoa(value));
	uart_send_string(" ");
	MAKE_FLOAT(value, 1, -127, 7ul << 12);
	uart_send_string(ftoa(value));
	uart_send_string(" ");
	MAKE_FLOAT(value, 0, 128, 0);
	uart_send_string(ftoa(value));
	uart_send_string(" ");
	MAKE_FLOAT(value, 1, 128, 0);
	uart_send_string(ftoa(value));
	uart_send_string(" ");
	MAKE_FLOAT(value, 0, 128, 7ul << 12);
	uart_send_string(ftoa(value));
	uart_send_string(" ");
	MAKE_FLOAT(value, 1, 128, 7ul << 12);
	uart_send_string(ftoa(value));
	uart_send_string("\n");
}

static void test_double()
{
	double value;
	
	uart_send_string(dtoa(1.245));
	uart_send_string(" ");
	uart_send_string(dtoa(-9.36));
	uart_send_string(" ");
	uart_send_string(dtoa(1234567.0));
	uart_send_string(" ");
	uart_send_string(dtoa(1048576.0));
	uart_send_string(" ");
	uart_send_string(dtoa(-0.0000125));
	uart_send_string(" ");
	uart_send_string(dtoa(-0.000379));
	uart_send_string("\n");
	
	uart_send_string(dtoa(3.1415926e17));
	uart_send_string(" ");
	uart_send_string(dtoa(1.42857e38));
	uart_send_string("\n");
	
	uart_send_string(dtoa(0.0));
	uart_send_string(" ");
	uart_send_string(dtoa(-0.0));
	uart_send_string(" ");
	MAKE_DOUBLE(value, 0, -127, 0);
	uart_send_string(dtoa(value));
	uart_send_string(" ");
	MAKE_DOUBLE(value, 1, -127, 0);
	uart_send_string(dtoa(value));
	uart_send_string(" ");
	MAKE_DOUBLE(value, 0, -127, 7ul << 20);
	uart_send_string(dtoa(value));
	uart_send_string(" ");
	MAKE_DOUBLE(value, 1, -127, 7ul << 20);
	uart_send_string(dtoa(value));
	uart_send_string(" ");
	MAKE_DOUBLE(value, 0, 128, 0);
	uart_send_string(dtoa(value));
	uart_send_string(" ");
	MAKE_DOUBLE(value, 1, 128, 0);
	uart_send_string(dtoa(value));
	uart_send_string(" ");
	MAKE_DOUBLE(value, 0, 128, 7ul << 20);
	uart_send_string(dtoa(value));
	uart_send_string(" ");
	MAKE_DOUBLE(value, 1, 128, 7ul << 20);
	uart_send_string(dtoa(value));
	uart_send_string("\n");
}

void system_reset()
{
	// write an incorrect value to _wdtc to reset the system
	_wdtc = 0;
}

void main()
{
	uint8_t ch;
	
	uart_init();
	uart_send_string("BA45F5650 UART\n");
	
	uart_send_string("sizeof(float)=");
	uart_send_string(ltoa(sizeof(float)));
	uart_send_string(", ");
	uart_send_string("sizeof(double)=");
	uart_send_string(ltoa(sizeof(double)));
	uart_send_string("\n");
	
	test_float();
	test_double();
	
	while (1)
	{
		// the watchdog is enabled by default and cannot be disabled by software
		// therefore, the watchdog timer must be cleared periodically
		_clrwdt(); // this also clears _to and _pdf bit
		
		// receive from UART
		if (_rxif)
		{
			ch = _txr_rxr;
			if (isprint(ch))
			{
				uart_send_string("UART character: ");
				uart_send(ch);
				uart_send_string("\n");
				
				switch (ch)
				{
				case 'G':
					// Green LED control
					if (_pcc0) // Is the pin in input mode?
					{
						// set led pin to output mode
						_pcc0 = 0;
						_pc0 = 0;
					}
					else
					{
						// led on/off
						_pc0 = ~_pc0;
					}
					break;
				case 'R':
					// Red LED control
					if (_pcc4)
					{
						_pcc4 = 0;
						_pc4 = 0;
					}
					else
						_pc4 = ~_pc4;
					break;
				case 'r':
					uart_send_string("RESET\n");
					uart_wait_tx();
					system_reset();
					break;
				case 'W':
					// Warning LED control
					if (_pbc1)
					{
						_pbc1 = 0;
						_pb1 = 0;
					}
					else	
						_pb1 = ~_pb1;
					break;
				case 'w':
					uart_send_string("LVRF=");
					uart_send_string(ltoa(_lvrf)); // low voltage reset flag
					uart_send_string(", WRF=");
					uart_send_string(ltoa(_wrf)); // flag of software reset triggered by incorrectly writing _wdtc
					uart_send_string(", TO=");
					uart_send_string(ltoa(_to)); // watchdog reset flag (read-only)
					uart_send_string(", PDF=");
					uart_send_string(ltoa(_pdf)); // halt flag (read-only)
					uart_send_string("\n");
					
					// the following two bits can be cleared only by software
					_lvrf = 0;
					_wrf = 0;
					break;
				}
			}
			else
			{
				uart_send_string("UART data: ");
				uart_send_string(hexstr(ch, 2));
				uart_send_string("\n");
			}
		}
	}
}

由于在合泰单片机上,float和double只有尾数位数的差异,指数位数是一样长的,所以其实我们只需要实现标准的单精度浮点数double转字符串的dtoa函数。
如果需要转换float型非标准浮点数,先将float扩展为double,再调用dtoa函数就行了。
float(非标准浮点数)扩展为double(单精度浮点数)的方法:
保持符号位和指数位不变,在尾数位后面添加8个0。
反过来,如果是double转float,那就是直接去掉尾数的末尾8位。

char *ftoa(float value)
{
	double d;
	uint8_t mem[4];
	
	mem[0] = 0;
	memcpy(mem + 1, &value, 3);
	memcpy(&d, mem, 4);
	return dtoa(d);
}

在标准的51单片机上,float和double都是单精度浮点数。

你可能感兴趣的:(Win32,c语言,开发语言,浮点数,holtek)