Qt中double与float与16进制字符串之间的转换

  • Post author:
  • Post category:其他




前述

在一些需求中,我们需要读取一些二进制文件,比如java的class文件,图片文件等等。我们有时需要解析里面具体的内容。通过Qt的QFile类读取文件后,可以将其中的二进制读到一个QBytearray中,而我们可以通过QBytearray的toHex方法将其转换为16进制字符串。

而有些时候,这些数据会存储一些double和float类型的浮点数据的16进制形式,我们需要根据需求把16进制转换为double或float。

对于整数int形式,Qt中有明确的方法可以进行转换。这里我们用uint的类型,避免负数。

#include <QString>
int main(){
// do some things
	QString hex = "cafebabe";
	uint res = hex.toUInt(nullptr, 16);
	qDebug() << res;	// 3405691582
// do some things
}

但是,对于float和double类型的数据,QString没有对应的16进制转换方法。需要另辟蹊径。



需要知道

首先我们需要知道c++中double和float类型所占的空间。


double在内存中占8字节,float在内存中占4字节。


其次,我们需要知道一些东西:


int、uint占4字节


最后,我们需要知道QT中内置的类型:

qulonglong,占8字节


对于qulonglong,在qt中定义如下:

typedef long long qint64;           /* 64 bit signed */
typedef unsigned long long quint64; /* 64 bit unsigned */
typedef qint64 qlonglong;
typedef quint64 qulonglong;

(见qglobal.h)

qulonglong就是unsigned long long



float和double转16进制



基本思路

在Qt中,QString存在一个arg方法,这个方法有n多种重载,它的作用类似字符串模板函数,并带有进制转换的功能,arg函数的所有重载都有4个参数。

如下:


QString 
arg(const QString &a, int fieldWidth = 0, QChar fillChar = QLatin1Char(' ')) const
QString 
arg(qlonglong a, int fieldWidth = 0, int base = 10, QChar fillChar = QLatin1Char(' ')) const
QString 
arg(qulonglong a, int fieldWidth = 0, int base = 10, QChar fillChar = QLatin1Char(' ')) const
QString 
arg(long a, int fieldWidth = 0, int base = 10, QChar fillChar = QLatin1Char(' ')) const
QString 
arg(ulong a, int fieldWidth = 0, int base = 10, QChar fillChar = QLatin1Char(' ')) const
QString 
arg(int a, int fieldWidth = 0, int base = 10, QChar fillChar = QLatin1Char(' ')) const
QString 
arg(uint a, int fieldWidth = 0, int base = 10, QChar fillChar = QLatin1Char(' ')) const
QString 
arg(short a, int fieldWidth = 0, int base = 10, QChar fillChar = QLatin1Char(' ')) const
QString 
arg(ushort a, int fieldWidth = 0, int base = 10, QChar fillChar = QLatin1Char(' ')) const
QString 
arg(double a, int fieldWidth = 0, char format = 'g', int precision = -1, QChar fillChar = QLatin1Char(' ')) const
QString 
arg(char a, int fieldWidth = 0, QChar fillChar = QLatin1Char(' ')) const
QString 
arg(QChar a, int fieldWidth = 0, QChar fillChar = QLatin1Char(' ')) const
QString 
arg(QStringView a, int fieldWidth = 0, QChar fillChar = QLatin1Char(' ')) const
QString 
arg(QLatin1String a, int fieldWidth = 0, QChar fillChar = QLatin1Char(' ')) const
QString 
arg(Args &&... args) const

举个例子,我们需要将一个int类型的1000转换成16进制字符串,字符串长度8,左侧补0:

QString hex = QString("%1").arg(1000, 8, 16, QLatin1Char('0'));
qDebug() << hex;	// "000003e8"

这很简单,但遗憾的是,arg方法并没有第一个参数是float和double的重载。

于是乎,我们可以将float和double转换成arg重载函数可用的类型。

但是,如果单纯地强制转换,会造成数据丢失,我们需要操作内存,将float和double的指针转换成其他类型的指针,再解引用。

但这里需要注意的是:

float占4字节,我们可以把float指针转换成int指针。

但是,对于double,他占8字节,就不能用int或uint,存不下,int和uint只占4字节,类型转换会造成数据丢失,结果错误。


所以,double指针必须转换到占用空间相等或比他大的数据类型指针。

double占8字节,我们可以选择qulonglong类型进行转换。

float取地址后,转换为int指针或uint指针类型,,在解引用得到int或uint;

double取地址后,转为qulonglong指针类型,再解引用得到qulonglong。

float a = 1.23f;
double b = 1.234;
// float指针类型转换
uint a_uint = *(uint*)&a;
// double指针类型转换
qulonglong b_q = *(qulonglong*)&b

而后就可以放入arg了。

对于16进制转float和double,只需要逆着来,先把16进制使用toUInt方法转成uint,(double是toLonglong)再使用指针类型强制转换,得到float或double。



完整代码

// float转16进制字符串
float f = 20.321;
uint f_uint = *(uint*)&f;
QString f_hex = QString("%1").arg(f_uint, 4, 16, QLatin1Char('0'));	// 4是生成字符串的最小长度,可以改为8
qDebug() << f_hex;	// "41a29168"

// 16进制字符串转float
QString hex_float = "41a29168";
uint hex_uint = hex_float.toUInt(nullptr, 16);
float hex_res = *(float*)&hex_uint;
qDebug() << hex_res;	// 20.321

// double转16进制字符串
double d = 20.321;
qulonglong d_long = *(qulonglong*)&d;
QString d_hex = QString("%1").arg(d_long, 8, 16, QLatin1Char('0'));
qDebug() << d_hex;	// "4034522d0e560419"

// 16进制字符串转double
QString hex_double = "4034522d0e560419";
qulonglong hex_long = hex_double.toULongLong(nullptr, 16);
double hex_d = *(double*)&hex_long;
qDebug() << hex_d;	// 20.321



版权声明:本文为SiriusWilliam原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。