unsigned long to char

Discussion in 'Programmer's Corner' started by MaxD, May 26, 2012.

  1. MaxD

    Thread Starter New Member

    May 5, 2012
    11
    0
    how to change unsigned long to char?
    Code ( (Unknown Language)):
    1.  
    2. unsigned long currentData = 12345678;
    3. char res[4];
    4.  
    5. res[0] = currentData & 0x000000FF;
    6. res[1] = (currentData & 0x0000FF00) >>8;
    7. res[2] = (currentData & 0x00FF0000) >>16;
    8. res[3] = (currentData & 0xFF000000) >>24;
    9.  
    what I expect:
    res[0] = 78
    res[1] = 65
    res[2] = 34
    res[3] = 12

    But I can't get the correct value. Why?
     
  2. t06afre

    AAC Fanatic!

    May 11, 2009
    5,939
    1,222
    Google Union Declarations or union c ;)
     
  3. hgmjr

    Moderator

    Jan 28, 2005
    9,030
    214
    The use of casting should make overcome your immediate problem.

    hgmjr
     
  4. MaxD

    Thread Starter New Member

    May 5, 2012
    11
    0
    hgmjr, what do you mean? Is it by using pointer?
     
  5. hgmjr

    Moderator

    Jan 28, 2005
    9,030
    214
    No. Casting forces the compiler to convert from the long type to a char type. Google C-language and casting.

    hgmjr
     
  6. WBahn

    Moderator

    Mar 31, 2012
    17,743
    4,790
    Guys! Guys!

    Look at what he is "expecting" to have happen!

    The problem has NOTHING to do with unions or shifts or casts! The problem has to do with fundamental number representations.

    To the OP: You are expecting binary shift operations to somehow shift out pairs of decimal digits. But it is shifing out pairs of hexademical digits. To see what is going to happen, you need to consider that 12345678 is represented as a binary value (or, for convenience, as a hex value):

    12345678 = 0x00BC614E

    Shifting by 4 bits is the same as shifting by 1 hex digit. So the code shifts by {0,2,4,6} digits respectively.

    So the code is going to result in:

    res[0] = 0x4E (78)
    res[1] = 0x61 (97)
    res[2] = 0xBC (188)
    res[3] = 0x00 (00)

    The fact that res[0] turns out to be 78 and what you wanted/expected it to be was also 78 is a pure coincidence and is due solely to the fact that 12345600 is evidently evenly divisible by 256.

    Does that match what you are seeing?

    If so, the next question is whether you are just trying to understand what the code is doing and why, or if you are specifically trying to separate out pairs of decimal digits from the value and store them in separate variables.

    If you are trying to separate out the pairs of decimal digits, we can move the discussion to accomplishing that.
     
    absf likes this.
  7. MrChips

    Moderator

    Oct 2, 2009
    12,440
    3,361
    Use a union:

    Code ( (Unknown Language)):
    1.  
    2. union data
    3. {
    4.   unsigned long b32;
    5.   unsigned char res[4];
    6. } currentData;
    7.  
    8. // then you can access currentData.b32
    9. // or currentData.res[0]
    10.  
    11.  
     
  8. hgmjr

    Moderator

    Jan 28, 2005
    9,030
    214
    I think that Wbahn has hit on the correct view of this problem. The variable has been assigned the decimal value 12345678. If the value had been assigned the value 0x12345678. The union would work.

    hgmjr
     
  9. MaxD

    Thread Starter New Member

    May 5, 2012
    11
    0
    WBahn, yeah, you are right. I got the result as 0x00BC614E. I know what you mean, so I need to convert the long type value into hexa before put into the array?

    hgmjr, assign the value become 0x12345678 is hexa to hexa, of course that way will work. But what if "unsigned long currentData = 12345678" is a GIVEN VALUE, and then need to store into the char array. Any solution?
     
  10. WBahn

    Moderator

    Mar 31, 2012
    17,743
    4,790
    It depends on what it is you are trying to accomplish.

    Are you trying to take a multi digit decimal value and store pairs of decimal digits from that value in the four separate elements of the array?

    If so, why? How are you going to use the reg[] array? I can think of two obvious ways and how you do the conversions is very, very different depending on which of the two you are looking for.

    So please take a step back and explain what it is you are trying to do a bit more completely.
     
  11. MaxD

    Thread Starter New Member

    May 5, 2012
    11
    0
    I want to know the PIC micro-controller status (ON/OFF). The status is in binary form (or hexa). I have used long type so that I can read every PORT status on the PIC (each PORT contains 1 byte data).

    After that, store the 'status' into the array, send it to another PIC.(actually I'm implementing MODBUS protocol between PICs). Got another way to do it?

    I have try to use char type to read the status, then store into the array. If more than 8 bits then jump to next array. But seems like it doesn't work. =(
     
  12. WBahn

    Moderator

    Mar 31, 2012
    17,743
    4,790
    You keep using the term "hexa", which is new to me. My only guess is that it is referring to hexadecimal digits represented in ASCII. Is that what it is, or is it something else? Or are you using it as just a shorthand for hexadecimal?

    If the value in that unsigned long are simply several one-byte values from different ports all packed together and all you are trying to do is split out the bytes into four elements of a one-byte (i.e., char) array, then using the union is almost certainly the cleanest way to do it.

    One thing that might be causing you confusion (or perhaps it is already cleared up), if the values on the four ports are (in decimal) 12, 34, 56, and 78, then the value stored in the unsigned long (MSB to LSB) will NOT be 12345678. Instead, they will be as follows:

    12 = 0x0C
    34 = 0x22
    56 = 0x38
    78 = 0x4E

    So the value stored will be 0x0C22384E, which is 203569230 in decimal.

    When you break these out into the char array (either using a union or just using the code you had in the first post), you will get the results you expected.
     
  13. MrChips

    Moderator

    Oct 2, 2009
    12,440
    3,361
    MaxD has a misunderstanding that is common place.

    All data in the computer is in binary.

    Binary, hexadecimal and decimal are all representations of the same data
    and are of consideration when one wants to display the value.

    There is no hexadecimal to decimal conversion required unless one wants to show the value to a human reader.

    There are only 10 types of people in the world. Those who understand binary and those who don't.
     
  14. coldpenguin

    Active Member

    Apr 18, 2010
    165
    9
    Define a union, that has a char[] with the size (plus a bit possibly) of the data that you think you will be transferring.

    Then on the other part of the union, define a struct or array with the data you want to send.

    On the other side of the transfer, assuming that you have the same compiler, and same architecture, you should be realtively 'safe' to define a similar union to decode the data. You put the data you want to transfer into the struct or array, but you send the char[].
    On the other end, you receive the data, and dump this into the char[], then you read out the 'decoded' data from the struct.
    The union of:
    union c{
    {
    char b[2];
    };
    {
    int a;
    };
    };
    could almost be thought of as two references/pointers pointing to the same section of memory.

    As a simple example, I am using this to define some transfer to and from two PICs over USART, saving to an I2C eeprom, and also sending the data over USB to a PC:
    typedef union lampd {
    struct {
    unsigned char ID;
    unsigned char xpos;
    unsigned char ypos;
    unsigned char name[20];
    }status;
    unsigned char data[32];
    } lampd;

    I play with ID, XPOS,YPOS and the name, and then copy the data to and from the devices over the respective protocols. Currently data is larger than the amount of data I am storing....
    1) The eeprom writes in pages of 16 bytes, so aligning to a page makes sense
    2) As the data in the eeprom will be semi-permanent, I have left some padding so I can add new fields at a later date.
     
  15. hgmjr

    Moderator

    Jan 28, 2005
    9,030
    214
    What I believe you are attempting to do is use the 32-bit hexadecimal representation of the decimal number 12345678 in what is referred to as "packed-bcd" format. The packed-bcd format value would be 0x12345678. You can look up packed bcd in wikipedia for a more detailed explanation.

    To get from the decimal value 12345678 to the packed-bcd format value 0x12345678 requires either a call to a built-in function from a library that performs the conversion or a call to a function that you write yourself that performs the conversion.

    hgmjr
     
  16. WBahn

    Moderator

    Mar 31, 2012
    17,743
    4,790
    I could be wrong, but I really don't think that is what he is doing, at least not at the end of the day. He talks about reading the status of multiple 8-bit ports into a single unsigned long. That sounds like the value stored in each byte is independent of the values stored in other bytes (and hence be a packed-byte format). What he needs to be careful about is to avoid using them in a way such that the bits in one byte interact with bits in another. Either the union or shift and mask approach will achieve that. What he needs to avoid is thinking of that entire 32-bit entity as a single "value", which includes by printing it or displaying it using normal binary to decimal string conversions. If he displays or prints it, he needs to do so in a way that maintains the integrity of those byte boundaries. Printing using a binary to hexadecimal string conversion will do that.
     
  17. MaxD

    Thread Starter New Member

    May 5, 2012
    11
    0
    Sorry my mistake, ok let me revise my question I ask before. So, what I want is how to (not to say change) EXTRACT the long type value into the char array, and then display the char array values.

    So, do you get what I am asking? Any solution for this?
    I think it is not difficult but I need to spend so much time on this -.-
     
  18. t06afre

    AAC Fanatic!

    May 11, 2009
    5,939
    1,222
    You have sevral solutions like using a union. The code how to do this has been shown to you. However you must take into consideration the endianess of your compiler. I think you are making this much harder then it is.
    http://en.wikipedia.org/wiki/Endianness
     
  19. hgmjr

    Moderator

    Jan 28, 2005
    9,030
    214
    One area of confusion seems to be tripping you up. Always keep in mind that the data in memory is stored in binary format. That is 1's and 0's. This is a pretty fundamental fact.

    Now, how you format that data for the purpose of displaying it for human consumption is entirely up to you.

    One popular format used to display the contents of a 32-bit word, for example, is a format referred to as ascii-hex. Ascii-hex allows you to express the content of a byte (8-bits) of memory with two ascii characters. The reason for the need of two ascii characters is that a single hexadecimal digit can take on any of the values:

    0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f

    Each of these hexadecimal digits represent a unique 4-bit binary value. Thus two of these digits are needed to represent the contents of a byte of memory. The minimum value for a byte would be 0x00 while the maximum value would be 0xff. I am using the c-language convention of representing a hexadecimal 8-bit number by using the prefix 0x.

    For example, to display the hexadecimal number 0xAB on a character LCD display would require you to send an ascii 0x41 followed by a ascii 0x42. These are the ascii codes expressed in hexadecimal values for the letter A and the letter B respectively.

    Hope this clears things up for you somewhat.

    hgmjr
     
  20. WBahn

    Moderator

    Mar 31, 2012
    17,743
    4,790
    Let's step back further and make sure that we understand what you have as a starting point and what you want to have happen as an ending point.

    Say one of the ports on the PIC has the following inputs from the msb of the port to the lsb of the port: {H,L,L,H,L,L,H,H}. This information is going to get embedded in your 32-bit unsigned long and now you want to extract that byte of information and do what with it? If you want to display it, what, exactly, would you want the user to see for this particular example?
     
Loading...