【问题标题】:QbyteArray to floatQbyteArray 浮动
【发布时间】:2016-08-19 22:59:48
【问题描述】:

我有一个将QByteArray 转换为float 的函数,它工作得非常好,除了当我输入零时,我得到5.87747e-39 而不是0.0

float QByteArrayToFloat(QByteArray f){
    bool ok;
    int sign = 1;

    f = f.toHex(); // Convert to Hex

    qDebug() << "QByteArrayToFloat: QByteArray hex = " << f;

    f = QByteArray::number(f.toLongLong(&ok, 16), 2);    // Convert hex to binary

    if(f.length() == 32) {
        if(f.at(0) == '1') sign =-1;     // If bit 0 is 1 number is negative
        f.remove(0,1);                   // Remove sign bit
    }

    QByteArray fraction = f.right(23);  // Get the fractional part
    double mantissa = 0;
    for(int i = 0; i < fraction.length(); i++){  // Iterate through the array to claculate the fraction as a decimal.
        if(fraction.at(i) == '1')
            mantissa += 1.0 / (pow(2, i+1));
    }

    int exponent = f.left(f.length() - 23).toLongLong(&ok, 2) - 127;     // Calculate the exponent

    qDebug() << "QByteArrayToFloat: float number = "<< QString::number(sign * pow(2, exponent) * (mantissa + 1.0),'f', 5);

    return (sign * pow(2, exponent) * (mantissa + 1.0));
}

QByteArrayisEmpty() 不起作用)中没有检查零的有用函数。我可以(在toHex() 之后)if(f.indexOf("00000000") == -1) return 0.0;if(exponent = -127 &amp;&amp; mantissa == 0) return 0.0;,但是有更优雅的解决方案吗?

另外,有趣的是QString::number(sign * pow(2, exponent) * (mantissa + 1.0),'f', 5); 可以正常工作并打印"0.00000"。但是,一旦我将其转换为使用toFloat(&amp;ok); 浮动,同样的事情就会发生。

【问题讨论】:

  • 如果字节数组包含浮点变量的内存表示,您可以使用QDataStream(f) &gt;&gt; myFloat 避免重新发明轮子。
  • @Murphy 内存表示是什么意思?我尝试了QDataStream 方法,但我得到0.0 作为所有输入的结果
  • 这就是浮点值在 RAM 中的存储方式,具体取决于您的编译器和架构如何处理它们。如果数组来自不同的来源,那么表示当然可能会有所不同;您可以尝试在读取流之前更改字节顺序是否可以解决此问题(QDataStream 的默认值是大端,使用英特尔机器您可能有小端)。
  • @Murphy 我试过 float a; QDataStream output(f); output.setByteOrder(QDataStream::LittleEndian); // and BigEndian output &gt;&gt; a; ,但我的所有输入仍然为零

标签: c++ qt floating-point qbytearray


【解决方案1】:

我从这个document引用:

非规范化数字

如果您有一个全为零位的指数字段,这就是 称为非规范化数。指数场为零, 你会认为真正的指数是-127,所以这个数字 将采用 1.MANTISA * 2-127 的形式,如上所述,但它 才不是。相反,它是 0.MANTISSA * 2-126。注意指数 不再是指数字段的值减去 127。它只是 -126。另请注意,我们不再为尾数包含隐含的一位。

您可以将零简单地视为另一个非规范化数字。零是 由零的指数和零的尾数表示。从我们的 理解非规范化数字,这转化为 0*2-126 = 0. 此符号位可以是正 (0) 或负 (1),导致正零或负零。这不是很重要 有数学意义,但这是允许的。

将您的代码仅针对零大小写更正为:

int exponent = - 126;
// and
return (sign * pow(2, exponent) * (mantissa + 0.0));

我得到答案 0.0

【讨论】:

  • 我尝试了int exponent = f.left(f.length() - 23).toLongLong(&amp;ok, 2) - 126; return (sign * pow(2, exponent) * (mantissa)); 并得到了零,但是当我尝试使用QByteArray hex = "3f6c5d8d" 时,我得到了0.84660 而不是0.923302
  • 0.923302 的指数字段不为零,即等于 01111110b。因此,您应该将代码用于非规范化数字(您的原始版本)。请read the document carefully
【解决方案2】:

我发现使用位移和reinterpret_cast 转换QByteArrayfloat 有更好的解决方案。它还处理零值。

float QByteArrayToFloat(QByteArray arr)
{
    static_assert(std::numeric_limits<float>::is_iec559, "Only supports IEC 559 (IEEE 754) float");

    quint32 temp = ((char)arr[0] << 24)|((char)arr[1] << 16)|((char)arr[2] << 8)|(char)arr[3]; // Big endian
    float* out = reinterpret_cast<float*>(&temp);

    return *out;
}

【讨论】:

  • 另一种以正确格式获取浮点数的相关方法。 stackoverflow.com/a/44075329/999943
  • 我认为不是 (char) 类型转换。我们必须使用 (quint8),因为它是 unsigned ov 更好,static_cast
猜你喜欢
  • 2019-08-10
  • 1970-01-01
  • 2015-01-11
  • 2013-12-25
  • 2018-02-25
  • 2023-03-24
  • 2014-03-07
  • 2012-12-17
  • 2012-02-05
相关资源
最近更新 更多