PDA

View Full Version : Converting to negative int - bug



ShamusVW
21st September 2010, 15:01
Hello
I am trying to use QString::toInt and have run into an issue that negative numbers get returned as zero.
Googling, I found http://bugreports.qt.nokia.com/browse/QTBUG-1098?page=com.atlassian.jira.plugin.system.issueta bpanels%3Aall-tabpanel.

Can anyone help me get round this issue?

I have in my code


QString fl;
fl = QString(m_DataYPoint.toHex()); //convert the data to string
intValue = fl.toInt(0,16); //...and the string to an int
m_YPoint = *(float*)&intValue;

where
m_DataYPoint is of type QByteArray
intValue of type qint32
m_YPoint of type float.

Thanks,
Shaun

wysota
21st September 2010, 15:13
So where is the negative value and why are you converting an integer to a float in such a weird and incorrect way?

ShamusVW
21st September 2010, 15:20
I am reading data from a socket stored in m_DataYPoint (QByteArray).
I want to convert it to floating point, and until I realised that I also have negative possibilities, it worked fine.

As an example, m_DataYPoint might contain "BDC6D604". This should equal "-0.09708..." but it gets returned as 0.

...what is the correct way...?

wysota
21st September 2010, 15:27
As an example, m_DataYPoint might contain "BDC6D604". This should equal "-0.09708..." but it gets returned as 0.
If BDC6D604 is not an integer then why are you converting it to an integer?


...what is the correct way...?
A correct way to cast an int to float is:

int a = 7;
float b = a;
Using pointers will cast pointers and make the internal representation of the integer be interpreted as an internal representation of a floating point value.

Anywho... If you read a float from the socket then first you have to ask the person who sent it what kind of encoding it uses (different platforms may have different encodings of floats or ints). Assuming it is the same as on your destination platform, simply do:

QByteArray ba("\xBD\xC6\xD6\x04",4);
float value;
memcpy(&value, ba.constData(), sizeof(float));

By the way, 0xBDC6D604 is 3,183,924,740 decimal (won't fit into 32 bit signed integer, so no way you would convert it through an int typed variable).

ShamusVW
21st September 2010, 15:42
I'm using IEEE-754 floating point.
Thanks, I will test it out.
Thanks for your input.

See http://babbage.cs.qc.edu/IEEE-754/32bit.html for correct conversion of floating point.

tbscope
21st September 2010, 16:01
That is something completely different than a int.

Look at documentation of IEEE-754 and the source code of that website to see how to convert it correctly.

toInt() certainly is not correct.

wysota
21st September 2010, 16:10
Just don't do the conversion (if you need any conversion at all!) by going through strings. If IEEE-754 is the officially used floating point representation then the code I gave you should work.

ShamusVW
21st September 2010, 20:25
Using pointers will cast pointers and make the internal representation of the integer be interpreted as an internal representation of a floating point value.


True - this is what I'm wanting, just not sure why negative is not working.
I have searched for ages looking for an efficient way to convert hex to floating point. When I originally implemented this solution, I'm not sure how I came upon it, but it worked. Unfortunately I was then only working in positive values.


Look at documentation of IEEE-754 and the source code of that website to see how to convert it correctly.
Ok, found the source! There seriously is not a shorter way to convert hex to float?



QByteArray ba("\xBD\xC6\xD6\x04",4);
float value;
memcpy(&value, ba.constData(), sizeof(float));

returns 0 again!

wysota
21st September 2010, 20:37
What is the contents of your byte array? Dump it to qDebug() please.

ShamusVW
22nd September 2010, 09:22
For a background of my application, see http://www.qtcentre.org/threads/25098-Modbus-implementation?p=121381&highlight=#post121381

I have a function


void Client::readSocket()
{
QByteArray block;
block = tcpSocket->readAll();
m_DataEccentricity[0] = block[9];
m_DataEccentricity[1] = block[10];
m_DataEccentricity[2] = block[11];
m_DataEccentricity[3] = block[12];
convertToFloat();
...
}

where


QByteArray m_DataEccentricity;


Convert to float...


void Client::convertToFloat()
{
QString fl;
fl = QString(m_DataEccentricity.toHex()); //convert the data to string
qint32 intValue = fl.toInt(0,16); //...and the string to an int
m_Eccentricity = *(float*)&intValue;
...
}


and in another function where I do updating of the GUI



eccentricityLineEdit->setText(QString::number(m_Eccentricity,'f',6));


With regard to contents of my byte array, doing a


qDebug (DataEccentricity)

gives "¾ _h", but that isn't a blank between 3/4 and _, it is supposed to be a vertical bar with a small bar midway up pointing to the right (like an "H" without the right bar) :)

wysota
22nd September 2010, 09:35
And using my code on it gives you 0?

ShamusVW
22nd September 2010, 09:48
Yes.
Even your example you suggested in your post returns 0



QByteArray ba("\xBD\xC6\xD6\x04",4);
float value;
memcpy(&value, ba.constData(), sizeof(float));


...but I would like result of -0.09708788990974426 (as per IEEE-754 spec)

SixDegrees
22nd September 2010, 10:22
I don't understand why you are bothering with all these conversions. Most of them are wrong and make no sense in light of what you're trying to achieve. But more importantly, the data as it sits in memory is already in the form you want it in. If you receive an IEEE float value, it's ALREADY in floating point format, and all you have to do is print it out, no conversions necessary, although you may have to jigger pointer types to ensure that it is interpreted by the streams operators correctly. But trying to hammer it into a hex value, then into an int value and finally into a float value is pointless; all you're doing is messing up data that was perfectly good to begin with.

The only issue that might arise is receiving data over a network with a machine whose endianess differs from network byte order. This sort of problem is unlikely - I would think the socket layer would take care of such things without your intervention - but if simply treating your value as a float directly doesn't work, try byte-swapping it and outputting those results.

Right now, you're making the problem way too hard. In fact, you're assuming there's a problem where there really isn't one.

wysota
22nd September 2010, 10:45
Even your example you suggested in your post returns 0
No, it doesn't.


#include <QtCore>

int main(int argc, char **argv){
QByteArray ba("\xBD\xC6\xD6\x04", 4);
float value;
memcpy(&value, ba.constData(), sizeof(float));
qDebug() << value;
return 0;
}

This yields 5.04937e-36.

ShamusVW
22nd September 2010, 11:07
Thanks for advice, but at the end of the day, I don't know how!
The original issue came up when I was trying to do a MODBUS application, and I got it to work back then by doing what I'm doing now.

Like so...?


m_DataEccentricity[0] = block[21];
m_DataEccentricity[1] = block[22];
m_DataEccentricity[2] = block[23];
m_DataEccentricity[3] = block[24];
QMessageBox box;
box.setText(m_DataEccentricity);
box.exec();


The camera (where we are reading inputs from) has gone down, so no way to test it right now.

Testing


QByteArray ba("\xBD\xC6\xD6\x04");
QMessageBox box;
box.setText(ba);
box.exec();

doesn't work though.

ShamusVW
22nd September 2010, 11:19
My apologies, you're right, it is 5.04937e-36 = 0.00...[x35]..504937, I had originally output it to a QMessageBox and rounded it to 6 figures, hence 0.
But it still isn't -0.09708788990974426

wysota
22nd September 2010, 11:23
Look, you can't blindly try different things without any knowledge on what you are doing. You have to read some documentation for the devices and software you are using. You have to know whether a device reports values in little endian or big endian, what is the size of its data type, etc. The same goes for the device where you accept those values. You can't just say "convert to IEEE-754" because the proper answer to this would be a question "convert from what?". Without knowing how your data is encoded there is no way of telling how to decode it.

I gave you a code that reads 4 bytes of data and interprets it as a floating point value in the format default for the target platform. There is no conversion involved, the data is simply interpreted as a specific type. If by repeating my process you get "0" then your initial byte array must have contained the representation of "0". If you expected some other value (whatever the encoding) then your byte array was probably invalid in the first place. Trying to display the byte array on a message box is a bad idea - the value is binary and not textual, it contains non-printable characters.

By the way, I doubt "float" can contain the value you want, it is a single precision floating point datatype.

ShamusVW
22nd September 2010, 11:49
I have values in my QByteArray. In each position I have 2 bytes as hex values. When I do the conversion manually (i.e. using a calculator) it gives the correct result, in the order that it is retrieved. However, this I did back then as a check. I then checked it on that website, and it gave correct results. I know about endianess (but for what I want to do, obviously in incorrect order!), and it is coming through in correct (Nope) order. When I run qDebug, I will get values like

qDebug() << QString::number(block[9],16);
qDebug() << QString::number(block[10],16);
qDebug() << QString::number(block[11],16);
qDebug() << QString::number(block[12],16);

it prints out

3d
7e
bf
26

Converting to floating point I get 0.062194012105464935 which is correct result using my method (albiet it maybe incorrect).
It is fine for you to say not to blindly try things without knowledge etc, etc. I appreciate this. But at this point I've been busy trying to get this to work on negative numbers for a long time. If then it is said to not convert anything, all in correct way to start with, then fine, I can accept that, but then how exactly do I use it? Because I've most certainly tried a lot of different things to get it to work, no luck. The values I have are hex, how do I get them to floating point, that will include negatives. I also tried the memcpy suggestion, it doesn't "appear" to work in the sense that I am getting 0, so what am I doing wrong.

I really do appreciate your help, and I don't want to have you stop it, understand I'm trying to keep up with your knowledge too.

ShamusVW
22nd September 2010, 12:02
Trying again...



QByteArray ab("\x3D\x7E\xBF\x26",4);
float value;
memcpy(&value, ab.constData(), sizeof(float));
qDebug() << value;


I get 1.32875e-15
On the website, it converts to 0.062194012105464935

HOWEVER... when I reverse it all as input to the website, i.e. 26BF7E3D, it returns 1.32875e-15

This also works for negatives!
So based on the way I was doing it (incorrectly) worked for positives, broke for negatives. I will have to reverse my positioning in my QByteArray and then the new method will work.

Thank you.
A very useful lesson :)
Shaun

wysota
22nd September 2010, 12:09
I know about endianess, and it is coming through in correct order.
Correct order for your calculations or for the destination platform?


If then it is said to not convert anything, all in correct way to start with, then fine, I can accept that, but then how exactly do I use it?
That's where the research part comes in. We are not going to do it for you.


Because I've most certainly tried a lot of different things to get it to work, no luck.
That's because you are counting on luck. Do it the scientific way instead. The first thing I would try would be to input the value you want to receive, dump its internal representation and compare it to your other representation.


The values I have are hex, how do I get them to floating point,
All values are hex. They are also oct, dec and binary. Hexadecimal only determines the base for encoding the value - your "floating point" is also "hex".


that will include negatives.
In what encoding? A binary (or hexadecimal, or whatever) value "becomes" negative only if you interpret it as such and again this depends on the encoding. 0x7F can be interpreted as "-1" but also as "+127". In the former encoding you could encode "+127" as 0x007F. In yet another encoding "-1" will be 0x80.


it doesn't "appear" to work in the sense that I am getting 0, so what am I doing wrong.
You are getting 0 because you lose precision while dumping the value. "0.1" cast to int will give "0".