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

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

程序下载链接: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 float2字节1位5位11位
float4字节1位8位23位
double8字节1位11位52位
long double16字节1位15位64或112位
合泰单片机浮点数格式
总长度符号位指数位尾数位
float3字节1位8位15位
double4字节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 <stdint.h>
#include <string.h>
#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, 可以在前面补0, 补够FTOA_PRECISION位数
	// 但如果发现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 <stdint.h>
#include <stdio.h>
#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;
}

程序运行结果:

然后再移植到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 <stdint.h>
#include <string.h>
#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 <stdint.h>
#include <BA45F5650.h>
#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 <ctype.h>
#include <stdint.h>
#include <string.h>
#include <BA45F5650.h>
#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都是单精度浮点数。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

巨大八爪鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值