Search code examples
c++qtfloating-pointqbytearray

QbyteArray to float


I have a function that converts QByteArray to float and it works perfectly fine except when I feed it zeros I get 5.87747e-39 instead of 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));
}

There is no useful function in QByteArray (isEmpty() doesn't work) that checks for zeros. I could do (after toHex()) if(f.indexOf("00000000") == -1) return 0.0; or if(exponent = -127 && mantissa == 0) return 0.0;, but is there a more elegant solution?

Also, what's interesting is that QString::number(sign * pow(2, exponent) * (mantissa + 1.0),'f', 5); works just fine and prints "0.00000". However, as soon as I transform it back to float with toFloat(&ok); same thing happens.


Solution

  • I've found out there's a better solution for QByteArray to float conversion using bit shifts and reinterpret_cast. It also handles the zero values.

    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;
    }