strange behavior convert HEX string to signed/unsigned long

Sep 7, 2011 at 2:35pm
Hello world ;)

I have a problem in c++ (compiler:g++, os:Debian) when I try to convert a HEX value as string into a signed/unsigned long. I thought I used the write way to do this.

Here are my function to work with the string i get from a HW-module:

1
2
3
4
5
6
7
8
9
10
11
unsigned long model::str_to_ulong(string data){
    int found = 0;
    unsigned long x;
    string temp("");
    do{
        found = data.find(",",found+1);
        temp += data.substr(found+1,2);
    }while(found!=-1);
    x = strtoul(temp.c_str(),NULL,16);
    return x;
};


1
2
3
4
5
6
7
8
9
10
11
signed long model::str_to_long(string data){
    int found = 0;
    signed long x;
    string temp("");
    do{
        found = data.find(",",found+1);
        temp += data.substr(found+1,2);
    }while(found!=-1);
    x = strtol(temp.c_str(),NULL,16);
    return x;
};


I take the data string with the format OK,XX,XX,XX,XX
XX is one byte in HEX. I have at list one byte and maximal 4 bytes of HEX. (8Bit,16Bit or 32Bit value)

In the do while i get ride of the "OK" and all the ",".

Now I have a temp string with this format: XXXXXXXX
where XXXXXXXX is a HEX value with 1,2, or 4 Byte(8Bit,16Bit or 32Bit value).

When I have to convert a signed value I use the strtol() function.
And when I have to convert a unsigned value I use the strtoul() function.

When i test my functions with the following code:

1
2
3
4
5
6
7
8
9
10
11
12
cout << "0xDE: " << str_to_long("OK,DE") << ", ";
cout << str_to_ulong("OK,DE") << ", ";
cout << "0xDEAD: " << str_to_long("OK,DE,AD") << ", ";
cout << str_to_ulong("OK,DE,AD") << ", ";
cout << "0xDEADBEEF: " << str_to_long("OK,DE,AD,BE,EF") << ", ";
cout << str_to_ulong("OK,DE,AD,BE,EF") << endl;
cout << "0xC0: " << str_to_long("OK,C0") << ", ";
cout << str_to_ulong("OK,C0") << ", ";
cout << "0xC0ED: " << str_to_long("OK,C0,ED") << ", ";
cout << str_to_ulong("OK,C0,ED") << ", ";
cout << "0xCOEDBABE: " << str_to_long("OK,C0,ED,BA,BE") << ", ";
cout << str_to_ulong("OK,C0,ED,BA,BE") << endl;


I get this results:


0xDE: 222, 222, 0xDEAD: 57005, 57005, 0xDEADBEEF: 2147483647, 3735928559
0xC0: 192, 192, 0xC0ED: 49389, 49389, 0xC0EDBABE: 2147483647, 3236805310


but I expected this results:


0xDE: -34, 222, 0xDEAD: -8531, 57005, 0xDEADBEEF: -559038737, 3735928559
0xC0: -64, 192, 0xC0ED: -16417, 49389, 0xC0EDBABE: -1058161986, 3236805310


I don't see the fault I'm doing when convert the values, any hint is welcome :) the unsigned value is correct, but there is a problem in the signed function. I got the unsigned values, except by the 4 Byte value, there i got the same output for 2 diffrent inputs... I tried to convert the values with a stringstream (strstrm << hex << temp; strstrm >> mysignedlong), but i got the same, wrong result. Could I solve my problem maybe by using a static_cast<signed long>(myvalue)?

thx for your help and hints

grz bluefire
Sep 7, 2011 at 3:04pm
0xDE: -34
0xDEAD: -8531
Why would you expect this? 0xDE and 0xDEAD both fit perfectly well into 32-bit integers.

For the 32-bit values, I don't know. Signed types behave weirdly with bitwise operations. Maybe change str_to_long() to this?
1
2
3
signed long model::str_to_long(const string &data){
    return (signed long)str_to_ulong(data);
}
Sep 7, 2011 at 3:05pm
2147483647 == 0x7FFFFFFF == highest signed 32-bit-value.

This should explain it...

The other two are ok as helios explained.


edit: biggest->highest, three->two
Last edited on Sep 7, 2011 at 3:07pm
Sep 7, 2011 at 3:31pm
thx for the fast answers.
Now i know where the 2147483647 value come from. thx

but at the other hand i still got my problem:

I expected to get (should be signed, is signed):

0xDE: -34
0xDEAD: -8531
0xDEADBEEF: -559038737


but this is my real output (should be signed, is unsigned):

0xDE: 222
0xDEAD: 57005
0xDEADBEEF: 2147483647


this are all unsigned values, but they should be signed!

when i use helios code i got the 32Bit value right, but the 16 & 8-Bit are still wrong:


0xDE: 222, 222, 0xDEAD: 57005, 57005, 0xDEADBEEF: -559038737, 3735928559
0xC0: 192, 192, 0xC0ED: 49389, 49389, 0xC0EDBABE: -1058161986, 3236805310


the first value should be signed and the second value should be unsigned.
It works with the 32Bit value, but not with the 16 & 8-bit.

ps: i expect this data from the following online converter:

http://www.binaryconvert.com/convert_signed_char.html for 8-Bit signed
http://www.binaryconvert.com/convert_signed_short.html for 16-Bit signed

maybe the online converter works wrong?

Sep 7, 2011 at 3:35pm
The links you posted are converting to truncated 8-bit and 16-bit representations, depending on user input. You are converting unconditionally to a 32-bit representation.
Sep 7, 2011 at 3:42pm
ok know i see my problem.
I have to search a possibility to convert first into an truncated 16 or 8-Bit value and then cast it into a 32-bit variable, this way i should get the right decimal value for the received HEX string. Is this a posible solution? Because i have to get the same values as the online converter. Some hints to do that are welcome :)
Sep 7, 2011 at 3:45pm
How about
1
2
3
4
5
6
7
8
unsigned long ul = str_to_ulong("OK,DE");
long l;
// depending on width
if(ul & 0x80)
    l = ul & 0x8000007F;
else
    l = ul;


Edit: u.U. l = (long)(ul & 0x8000007F);
Last edited on Sep 7, 2011 at 3:52pm
Sep 7, 2011 at 3:53pm
1
2
3
4
if (x<=0xFF)
    x=(signed char)x;
else if (x<=0xFFFF)
    x=(signed short)x;
Last edited on Sep 7, 2011 at 3:56pm
Sep 7, 2011 at 4:23pm
thx again for all your answers.
I got it to work like it should.

this is my working code at this moment:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
signed long model::str_to_long(string data){
    int found = 0;
    signed long x;
    string temp("");
    do{
        found = data.find(",",found+1);
        temp += data.substr(found+1,2);
    }while(found!=-1);
    x = strtol(temp.c_str(),NULL,16);
    if (x<=0xFF){
        return x=(signed char)x;
    }
    else if (x<=0xFFFF){
        return x=(signed short)x;
    }
    else{
        return (signed long)str_to_ulong(data);
    }
};


and this is my output, as expected:


0xDE: -34, 222, 0xDEAD: -8531, 57005, 0xDEADBEEF: -559038737, 3735928559
0xC0: -64, 192, 0xC0ED: -16417, 49389, 0xC0EDBABE: -1058161986, 3236805310


Sep 7, 2011 at 4:29pm
Don't the first few lines of str_to_long() do pretty much the same as str_to_ulong()? Why don't you just call that function, instead of duplicating code?
Sep 7, 2011 at 4:41pm
i need the "cleaning" of the string in some other functions.
so i took that part in a own function...
My code now looks like that:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
unsigned long model::str_to_ulong(string data){
    unsigned long x;
    string temp(cleanstr(data));
    x = strtoul(temp.c_str(),NULL,16);
    return x;
};
signed long model::str_to_long(string data){
    signed long x;
    string temp(cleanstr(data));
    x = strtol(temp.c_str(),NULL,16);
    if (x<=0xFF){
        return x=(signed char)x;
    }
    else if (x<=0xFFFF){
        return x=(signed short)x;
    }
    else{
        return (signed long)str_to_ulong(data);
    }
};
string model::cleanstr(string data){
    int found = 0;
    string temp("");
    do{
        found = data.find(",",found+1);
        temp += data.substr(found+1,2);
    }while(found!=-1);
    return temp;
};


is that what you meant with
Why don't you just call that function, instead of duplicating code
?
Sep 7, 2011 at 4:46pm
Egh. Good enough, I guess.
Topic archived. No new replies allowed.