Aman Kejriwal
Aman Kejriwal

Reputation: 549

Converting 32 bit number to four 8bit numbers

I am trying to convert the input from a device (always integer between 1 and 600000) to four 8-bit integers.

For example,

If the input is 32700, I want 188 127 00 00.

I achieved this by using:

32700 % 256 
32700 / 256 

The above works till 32700. From 32800 onward, I start getting incorrect conversions.

I am totally new to this and would like some help to understand how this can be done properly.

Upvotes: 3

Views: 16920

Answers (6)

Rakesh Menon
Rakesh Menon

Reputation: 27

I have been in a similar kind of situation while packing and unpacking huge custom packets of data to be transmitted/received, I suggest you try below approach:

typedef union 
{
   uint32_t u4_input;
   uint8_t  u1_byte_arr[4];
}UN_COMMON_32BIT_TO_4X8BIT_CONVERTER;

UN_COMMON_32BIT_TO_4X8BIT_CONVERTER un_t_mode_reg;
un_t_mode_reg.u4_input = input;/*your 32 bit input*/
// 1st byte = un_t_mode_reg.u1_byte_arr[0];
// 2nd byte = un_t_mode_reg.u1_byte_arr[1];
// 3rd byte = un_t_mode_reg.u1_byte_arr[2];
// 4th byte = un_t_mode_reg.u1_byte_arr[3];

Upvotes: 0

Aman Kejriwal
Aman Kejriwal

Reputation: 549

I ended up doing this:

unsigned char bytes[4];
unsigned long n;

n = (unsigned long) sensore1 * 100;

bytes[0] = n & 0xFF;     
bytes[1] = (n >> 8) & 0xFF;
bytes[2] = (n >> 16) & 0xFF;
bytes[3] = (n >> 24) & 0xFF;
       CAN_WRITE(0x7FD,8,01,sizeof(n),bytes[0],bytes[1],bytes[2],bytes[3],07,255);

Upvotes: 0

cup
cup

Reputation: 8299

It really depends on how your architecture stores an int. For example

  1. 8 or 16 bit system short=16, int=16, long=32
  2. 32 bit system, short=16, int=32, long=32
  3. 64 bit system, short=16, int=32, long=64

This is not a hard and fast rule - you need to check your architecture first. There is also a long long but some compilers do not recognize it and the size varies according to architecture.

Some compilers have uint8_t etc defined so you can actually specify how many bits your number is instead of worrying about ints and longs.

Having said that you wish to convert a number into 4 8 bit ints. You could have something like

unsigned long x = 600000UL;  // you need UL to indicate it is unsigned long
unsigned int b1 = (unsigned int)(x & 0xff);
unsigned int b2 = (unsigned int)(x >> 8) & 0xff;
unsigned int b3 = (unsigned int)(x >> 16) & 0xff;
unsigned int b4 = (unsigned int)(x >> 24);

Using shifts is a lot faster than multiplication, division or mod. This depends on the endianess you wish to achieve. You could reverse the assignments using b1 with the formula for b4 etc.

Upvotes: 5

PiCTo
PiCTo

Reputation: 974

Major edit following clarifications:

Given that someone has already mentioned the shift-and-mask approach (which is undeniably the right one), I'll give another approach, which, to be pedantic, is not portable, machine-dependent, and possibly exhibits undefined behavior. It is nevertheless a good learning exercise, IMO.

For various reasons, your computer represents integers as groups of 8-bit values (called bytes); note that, although extremely common, this is not always the case (see CHAR_BIT). For this reason, values that are represented using more than 8 bits use multiple bytes (hence those using a number of bits with is a multiple of 8). For a 32-bit value, you use 4 bytes and, in memory, those bytes always follow each other.

We call a pointer a value containing the address in memory of another value. In that context, a byte is defined as the smallest (in terms of bit count) value that can be referred to by a pointer. For example, your 32-bit value, covering 4 bytes, will have 4 "addressable" cells (one per byte) and its address is defined as the first of those addresses:

|==================|
| MEMORY | ADDRESS |
|========|=========|
|  ...   |   x-1   | <== Pointer to byte before
|--------|---------|
| BYTE 0 |    x    | <== Pointer to first byte (also pointer to 32-bit value)
|--------|---------|
| BYTE 1 |   x+1   | <== Pointer to second byte
|--------|---------|
| BYTE 2 |   x+2   | <== Pointer to third byte
|--------|---------|
| BYTE 3 |   x+3   | <== Pointer to fourth byte
|--------|---------|
|  ...   |   x+4   | <== Pointer to byte after
|===================

So what you want to do (split the 32-bit word into 8-bits word) has already been done by your computer, as it is imposed onto it by its processor and/or memory architecture. To reap the benefits of this almost-coincidence, we are going to find where your 32-bit value is stored and read its memory byte-by-byte (instead of 32 bits at a time).

As all serious SO answers seem to do so, let me cite the Standard (ISO/IEC 9899:2018, 6.2.5-20) to define the last thing I need (emphasis mine):

Any number of derived types can be constructed from the object and function types, as follows:

  • An array type describes a contiguously allocated nonempty set of objects with a particular member object type, called the element type. [...] Array types are characterized by their element type and by the number of elements in the array. [...]
  • [...]

So, as elements in an array are defined to be contiguous, a 32-bit value in memory, on a machine with 8-bit bytes, really is nothing more, in its machine representation, than an array of 4 bytes!

Given a 32-bit signed value:

int32_t value;

its address is given by &value. Meanwhile, an array of 4 8-bit bytes may be represented by:

uint8_t arr[4];

notice that I use the unsigned variant because those bytes don't really represent a number per se so interpreting them as "signed" would not make sense. Now, a pointer-to-array-of-4-uint8_t is defined as:

uint8_t (*ptr)[4];

and if I assign the address of our 32-bit value to such an array, I will be able to index each byte individually, which means that I will be reading the byte directly, avoiding any pesky shifting-and-masking operations!

uint8_t (*bytes)[4] = (void *) &value;

I need to cast the pointer ("(void *)") because I can't bear that whining compiler &value's type is "pointer-to-int32_t" while I'm assigning it to a "pointer-to-array-of-4-uint8_t" and this type-mismatch is caught by the compiler and pedantically warned against by the Standard; this is a first warning that what we're doing is not ideal!

Finally, we can access each byte individually by reading it directly from memory through indexing: (*bytes)[n] reads the n-th byte of value!

To put it all together, given a send_can(uint8_t) function:

for (size_t i = 0; i < sizeof(*bytes); i++)
    send_can((*bytes)[i]);

and, for testing purpose, we define:

void send_can(uint8_t b)
{
    printf("%hhu\n", b);
}

which prints, on my machine, when value is 32700:

188
127
0
0

Lastly, this shows yet another reason why this method is platform-dependent: the order in which the bytes of the 32-bit word is stored isn't always what you would expect from a theoretical discussion of binary representation i.e:

  • byte 0 contains bits 31-24
  • byte 1 contains bits 23-16
  • byte 2 contains bits 15-8
  • byte 3 contains bits 7-0

actually, AFAIK, the C Language permits any of the 24 possibilities for ordering those 4 bytes (this is called endianness). Meanwhile, shifting and masking will always get you the n-th "logical" byte.

Upvotes: 5

Yunnosch
Yunnosch

Reputation: 26783

You could do some bit masking.

600000 is 0x927C0

600000 / (256 * 256) gets you the 9, no masking yet.
((600000 / 256) & (255 * 256)) >> 8 gets you the 0x27 == 39. Using a 8bit-shifted mask of 8 set bits (256 * 255) and a right shift by 8 bits, the >> 8, which would also be possible as another / 256.
600000 % 256 gets you the 0xC0 == 192 as you did it. Masking would be 600000 & 255.

Upvotes: 0

CiaPan
CiaPan

Reputation: 9570

The largest positive value you can store in a 16-bit signed int is 32767. If you force a number bigger than that, you'll get a negative number as a result, hence unexpected values returned by % and /.

Use either unsigned 16-bit int for a range up to 65535 or a 32-bit integer type.

Upvotes: -1

Related Questions