背景:由于先前正在用Qt做上位机实现从STM32中发送字节,从而在Qt上位机上接收并转换为浮点型来进行保存。查阅了很多资料想了很多的解决方案。
有关浮点数在内存中的表示:
浮点型数据在存储方式上遵从IEEE规范。符号位、指数位、尾数部分三部分组成
说明:
1.有效数据位最高位必须为1,因为只能为1,所以不必存储,而剩下的所有位称为尾数部分,是需要存储在内存中的数据
2.R32.24和R64.53的存储方式都是用科学计数法来存储数据的。
float的存储方式:
float单精度浮点型遵从IEEER32.24(即32位形式存储,4字节,有效数据位为24位)
31 30 23 220S EEEEEEEE DDDDDDDDDDDDDDDDDDDDDDD
float转换为内存的步骤:
1>先将实数绝对值转换为对应二进制格式
2>将二进制格式实数小数点向左或右移n位,直到小数点移动到第高位的“1”右边
3>从小数点右边第一位开始数出23位数字放入第22到第0位(实现尾数部分存储)
4>根据原实数的正负,给符号位,即最高位的第31位赋值,为正则“0”,为负则“1”
5>根据小数点移动方向判断指数正负,给指数位最高位(第30位赋值),左移为正,第30位放"1",右移或没移为负,放入"0"
6>如果为左移得到:(n-1)的值化为二进制,并在左边加"0"补足7位,放入第29到第23位.
如果为右移或没移动(n=0):n化为二进制在左边加"0"补足7位,再各位取反,再放入第29到23位
例:12.5转换为字节:
(1)12.5实数绝对值转换为二进制为1100.1
(2)向左移动3位,转换为科学计数法1.1001E3,n=3
(3)将小数点右边第一位开始输出23位放入到第22位到第0位,数据为D存储形式:
DDDDDDDDDDDDDDDDDDDDDDD10010000000000000000000
(4)12.5为正数,最高位,即第31位置"0",S=0
(5)n左移得到尾数部分,指数为正,第30位置"1"
(6)n-1=2=0010,向左加"0"补足7位,放入第29到第23位
所以,指数位E为:
EEEEEEEE10000010
31 30 23 220S EEEEEEEE DDDDDDDDDDDDDDDDDDDDDDD0 10000010 10010000000000000000000
存储字节数据转换为float步骤:
将一个内存存储的float二进制格式转换为十进制步骤:
(1)将第22到第0位的二进制数写出来,在最左边补一位"1",得到二十四位有效数组.将小数点在最左边那个"1"的右边
(2)取出第29到第第23位所表示的值n.当30位是"0",将各位取反.当30位是1"时n增加1
(3)当第30位是"0时,将小数点左移n位;当第30位是"1"时,右移n位,得到一个二进制表示的实数
(4)将这个二进制实数化为十进制,并根据第31位是"0"还是"1"加上正负号即可.
同样以12.5的二进制为例:
(1)在最左边补"1",得到二十四位有效数字。将小数点点在最左边的“1””右边
1.10010000000000000000000
(2) 取出第29到第23位所表示的值n。由于30位是“1”,所有将n增1为0000011(即n=3)
(3) 由于30位是“1”,将小数点右移3位,得到二进制实数为:
1100. 10000000000000000000
(4) 转换为十进制数,由于31位为“0”,所有结果为12.5
double的存储方式:(与float原理同)
double双精度浮点型遵从IEEER64.53(即64位形式存储,8字节,有效数据位为53位)
言归正传,在搞明白了浮点型的存储形式之后,单片机将浮点数转换为字节发送给上位机,上位机接收后可以根据十六进制数据显示其存储形式。虽然了解了二进制如何转变取回浮点型的步骤。但是,难道我还要专门写一个算法来进行取位、判断、计算操作来转换吗,显然我觉得一定有什么办法可以实现更简单的转换。
基于计算机内部对数据类型的解码,我突然想到是不是只要定义一个浮点型的变量,将内存形式放入其中,再去调用和使用这个变量,是不是就会输出的是内存形式对应解码的那个浮点型呢?经测试,内存拷贝无疑就是最好的方法!
内存拷贝函数memcpy
memcpy
原型:extern void *memcpy (void *dest,void *src, unsigned int count);
功能:由src所指内存区域复制count个字节到dest所指内存区域
说明:sec和dest所指内存区域不能重叠,函数返回指向dest的指针。
作用:用来做内存拷贝,可以拿它拷贝任何数据类型的对象,可以指定拷贝数据长度
QByteArray buffer; float map_x=0;QByteArray x = buffer.mid(1,4);memcpy(&map_x, x.data(), 4);
与memcpy类似的其他函数拓展:
strcpy
原型:extern char*strcpy(char* dest ,char*src)
功能:把src所指由NULL结束的字符串复制到dest所指的数组中。
说明:src和dest所指内存区域不可以重叠且dest必须有足够的空间来容纳src的字符串,f返回指向 dest的指针
作用:只能拷贝字符串,遇到'/0'就结束拷贝
memset
原型:extern void * memset(void *buffer, int c , int count)
buffer: 指针或数组c,赋给buffer的值,count:buffer的长度
作用:将已开辟内存空间buffer的首count个字节的值设为值c;
常用于内存空间初始化:
方便清空结构类型的变量或数据
总结:
memset主要应用是初始化某个内存空间。
memcpy是用于copy源空间的数据到目的空间中。
strcpy用于字符串copy,遇到‘/0’,将结束。
参考资料:
(30条消息) 浮点数与字节数据转换详解_wsqyouth的博客-CSDN博客_字节转浮点数
:内含浮点型和字节数据的转换例子细节,本文大多内容由本文总结
n浮点型数据(float, double)存储IEEE标准解析和应用 - zhugehq - 博客园 ()
:内含对浮点型数据存储形式中数值的存放对E指数位的值有另一种解释:“E位的编码形式并非常用的补码形式(正数是它本身,负数符号位变1数据取反加一),而是把E位的8位能代表的数据空间(0-255)左右分为两半,以127为中点,代表0。如果指数为是1,则E位是128;如果指数位是2,则E位是129;如果指数位是-1,则E位是126,以此类推。”
浮点数在计算机中存储方式 - Robin Zhang - 博客园 ()
:说明浮点型转换过程中可能会出现的误差和原因分析
(31条消息) Qt总结之二十三:QByteArray与char、int、float(及其数组)之间的互相转化_ooMelloo的博客-CSDN博客_qbytearray转qint64