I got some vectors containing unsigned chars that represent pixels from a frame.
I got this function working without the MMX improvement, but I frustrated whit MMX that doesnt work ... So:
I need to add two unsigned chars (the sum need to be done as a 16bit instead of a 8bit cause unsigned char goes from 0-255 as known) and divide them by two (shift right 1). The code I have done so far is below, but the values are wrong, the adds_pu16 doesnt add the 16bit just 8:
MM0 = _mm_setzero_si64(); //all zeros
MM1 = TO_M64(lv1+k); //first 8 unsigned chars
MM2 = TO_M64(lv2+k); //second 8 unsigned chars
MM3 =_mm_unpacklo_pi8(MM0,MM1); //get first 4chars from MM1 and add Zeros
MM4 =_mm_unpackhi_pi8(MM0,MM1); //get last 4chars from MM1 and add Zeros
MM5 =_mm_unpacklo_pi8(MM0,MM2); //same as above for line 2
MM6 =_mm_unpackhi_pi8(MM0,MM2);
MM1 = _mm_adds_pu16(MM3,MM5); //add both chars as a 16bit sum (255+255 max range)
MM2 = _mm_adds_pu16(MM4,MM6);
MM3 = _mm_srai_pi16(MM1,1); //right shift (division by 2)
MM4 = _mm_srai_pi16(MM2,1);
MM1 = _mm_packs_pi16(MM3,MM4); //pack the 2 MMX registers into one
v2 = TO_UCHAR(MM1); //put results in the destination array
New developments:
Thanks for that king_nak!!
I wrote a simple version of what I am trying to do:
int main()
{
char A[8]={255,155,2,3,4,5,6,7};
char B[8]={255,155,2,3,4,5,6,7};
char C[8];
char D[8];
char R[8];
__m64* pA=(__m64*) A;
__m64* pB=(__m64*) B;
__m64* pC=(__m64*) C;
__m64* pD=(__m64*) D;
__m64* pR=(__m64*) R;
_mm_empty();
__m64 MM0 = _mm_setzero_si64();
__m64 MM1 = _mm_unpacklo_pi8(*pA,MM0);
__m64 MM2 = _mm_unpackhi_pi8(*pA,MM0);
__m64 MM3 = _mm_unpacklo_pi8(*pB,MM0);
__m64 MM4 = _mm_unpackhi_pi8(*pB,MM0);
__m64 MM5 = _mm_add_pi16(MM1,MM3);
__m64 MM6 = _mm_add_pi16(MM2,MM4);
printf("SUM:\n");
*pC= _mm_add_pi16(MM1,MM3);
*pD= _mm_add_pi16(MM2,MM4);
for(int i=0; i<8; i++) printf("\t%d ", (C[i])); printf("\n");
for(int i=0; i<8; i++) printf("\t%d ", D[i]); printf("\n");
printf("DIV:\n");
*pC= _mm_srai_pi16(MM5,1);
*pD= _mm_srai_pi16(MM6,1);
for(int i=0; i<8; i++) printf("\t%d ", (C[i])); printf("\n");
for(int i=0; i<8; i++) printf("\t%d ", D[i]); printf("\n");
MM1= _mm_srai_pi16(MM5,1);
MM2= _mm_srai_pi16(MM6,1);
printf("Final Result:\n");
*pR= _mm_packs_pi16(MM1,MM2);
for(int i=0; i<8; i++) printf("\t%d ", (R[i])); printf("\n");
return(0);
}
And the results are:
SUM:
-2 1 54 1 4 0 6 0
8 0 10 0 12 0 14 0
DIV:
-1 0 -101 0 2 0 3 0
4 0 5 0 6 0 7 0
Final Result:
127 127 2 3 4 5 6 7
Well the small numbers are ok while the big numbers which give 127 are wrong. This is a problem, what am I doing wrong :s
You should switch the operands in the _mm_unpacklo_pi8 calls. As you do it, the value bytes are in the higher bytes of the word (e.g. AB and 00 packed to AB00). After addition and shifting, the values will be greater then 0x7F, und thus saturated to that value by the pack instruction.
With switched operands, the math is done on values like 00AB, and the result will fit into a signed byte.
UPATE:
After your additional info, I see that the problem is with the _mm_packs_pi16. This is the assembly instruction packsswb, which will saturate signed bytes. E.g. Values > 127 will be set to 127. (255+255)>>1 is 255, and (155+155)>>1 is 155...
Use _mm_packs_pu16 instead. This treats the values as unsigned bytes, and you get the desired results (255/155).
I think i found the problem:
The arguments of the unpack instructions are in the wrong order. If you look at the registers as a whole, it looks like the individual chars are zero-extended to shorts, but in fact, they are zero-padded. Just swap around mm0 and the other register in each case and it should work.
Also, you don't need saturated add, a normal PADDW is sufficient. The maximum value you will get is 0xff+0xff=0x01fe, which doesn't have to be saturated.
Edit: What's more, PACKSSWB doesn't quite do what you want. PACKUSWB is the correct instruction, saturation will get you wrong results.
Here's a solution (Also replaced the shifts with logical ones and used different pseudo-registers in some places):
mm0=pxor(mm0,mm0) =[00,00,00,00,00,00,00,00]
mm1 =[a0,10,ff,18,7f,f0,ff,cc]
mm2 =[c0,20,ff,00,70,26,ff,01]
mm3=punpcklbw(mm1,mm0) =[00a0,0010,00ff,0018]
mm4=punpckhbw(mm1,mm0) =[007f,00f0,00ff,00cc]
mm5=punpcklbw(mm2,mm0) =[00c0,0020,00ff,0000]
mm6=punpckhbw(mm2,mm0) =[0070,0026,00ff,0001]
mm5=paddw(mm3,mm5) =[0160,0030,01fe,0018]
mm6=paddw(mm4,mm6) =[00ef,0116,01fe,00cd]
mm3=psrlwi(mm5,1) =[00b0,0018,00ff,000c]
mm4=psrlwi(mm6,1) =[0077,008b,00ff,0066]
mm1=packuswb(mm3,mm4) =[b0,18,ff,0c,77,8b,ff,66]
As an aside, you don't need a 16 bit intermediate to calculate the average of two 8 bit values. The formulation:
(a >> 1) + (b >> 1) + (a & b & 1)
gives the correct result with only 8 bit intermediates necessary. Perhaps you can utilise this to improve your throughput, if you have 8 bit vector instructions available.
Related
unsigned char lines[64000][64];
int RandomNumberGenerator(const int nMin, const int nMax);
int main(void) {
srand(time(NULL));
for(int i=0; i<64000; i++){
for(int j=0; j<64; j++) {
lines[i][j] = rand();
}
}
TL;DR: My main goal is to cause a random bit flip in a random (row, column) in this 2D array.
I have a 2D array filled with random numbers and my goal is to cause a bit flip on a random (row, column) or element. I understand getting the random row and column but I am not sure how do I should do a bit flip on that address or element.
Edit: The way I am treating this, lines[i][j] is 8 bits or a byte. And I want to flip one of the bits in a byte.
XOR operation
look at bitwise XOR truth table
Note that : if you make an XOR for single bit with 1 it will flipping and if you make
XOR with 0 it will be the same
c/c++ languages give you the ability to make the XOR with ^
for example
for flipping a single bit in byte
unsigned char x = 153 ; //x have this 0b10011001
x ^= (1<<5); // this will flipping bit 5 so x will be 0b10111001
// not that (1<<5) equal to 0b00100000
for flipping more than one bit in byte
unsigned char x = 153 ; //x have this 0b10011001
x ^= 0b00101000; // this will flipping bits 3,5 so x will be 0b10110001
// you could write this 0b00101000 in any representation you like
// (1<<5)&(1<<3) or 0x28 or as dismal 40
for flipping all bits in Byte
unsigned char x =153 ; // binarry equvelant to 0b10011001
x ^= 255 ; // after this statment x will be 0b01100110 means all bits fliped
// note that : 255 is equal to 0b11111111
I'm doing an assignment for school to swap the bytes in an unsigned long, and return the swapped unsigned long. ex. 0x12345678 -> 0x34127856.
I figured I'll make a char array, use sprintf to insert the long into a char array, and then do the swapping, stepping through the array. I'm pretty familiar with c++, but C seems a little more low level. I researched a few topics on sprintf, and I tried to make an array, but I'm not sure why it's not working.
unsigned long swap_bytes(unsigned long n) {
char new[64];
sprintf(new, "%l", n);
printf("Char array is now: %s\n", new);
}
TLDR; The correct approach is at the bottom
Preamble
Issues with what you're doing
First off using sprintf for byte swapping is the wrong approach because
it is a MUCH MUCH slower process than using the mathematical properties of bit operations to perform the byte swapping.
A byte is not a digit in a number. (a wrong assumption that you've made in your approach)
It's even more painful when you don't know the size of your integer (is it 32-bits, 64 bits or what)
The correct approach
Use bit manipulation to swap the bytes (see way way below)
The absolutely incorrect implementation with wrong output (because we're ignoring issue #2 above)
There are many technical reasons why sprintf is much slower but suffice it to say that it's so because moving contents of memory around is a slow operation, and of course more data you're moving around the slower it gets:
In your case, by changing a number (which sits in one manipulatable 'word' (think of it as a cell)) into its human readable string-equivalence you are doing two things:
You are converting (let's assume a 64-bit CPU) a single number represented by 8 bytes in a single CPU cell (officially a register) into a human equivalence string and putting it in RAM (memory). Now, each character in the string now takes up at least a byte: So a 16 digit number takes up 16 bytes (rather than 8)
You are then moving these characters around using memory operations (which are slow compared do doing something directly on CPU, by factor of a 1000)
Then you're converting the characters back to integers, which is a long and tedious operation
However, since that's the solution that you came up with let's first look at it.
The really wrong code with a really wrong answer
Starting (somewhat) with your code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
unsigned long swap_bytes(unsigned long n) {
int i, l;
char new[64]; /* the fact that you used 64 here told me that you made assumption 2 */
sprintf(new, "%lu", n); /* you forgot the `u` here */
printf("The number is: %s\n", new); /* well it shows up :) */
l = strlen(new);
for(i = 0; i < l; i+=4) {
char tmp[2];
tmp[0] = new[i+2]; /* get next two characters */
tmp[1] = new[i+3];
new[i+2] = new[i];
new[i+3] = new[i+1];
new[i] = tmp[0];
new[i+1] = tmp[1];
}
return strtoul(new, NULL, 10); /* convert new back */
}
/* testing swap byte */
int main() {
/* seems to work: */
printf("Swapping 12345678: %lu\n", swap_bytes(12345678));
/* how about 432? (err not) */
printf("Swapping 432: %lu\n", swap_bytes(432));
}
As you can see the above is not really byte swapping but character swapping. And any attempt to try and "fix" the above code is nonsensical. For example,how do we deal with odd number of digits?
Well, I suppose we can pad odd digit counts with a zero:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
unsigned long swap_bytes(unsigned long n) {
int i, l;
char new[64]; /* the fact that you used 64 here told me that you made assumption 2 */
sprintf(new, "%lu", n); /* you forgot the `u` here */
printf("The number is: %s\n", new); /* well it shows up :) */
l = strlen(new);
if(l % 2 == 1) { /* check if l is odd */
printf("adding a pad to make n even digit count");
sprintf(new, "0%lu", n);
l++; /* length has increased */
}
for(i = 0; i < l; i+=4) {
char tmp[2];
tmp[0] = new[i+2]; /* get next two characters */
tmp[1] = new[i+3];
new[i+2] = new[i];
new[i+3] = new[i+1];
new[i] = tmp[0];
new[i+1] = tmp[1];
}
return strtoul(new, NULL, 10); /* convert new back */
}
/* testing swap byte */
int main() {
/* seems to work: */
printf("Swapping 12345678: %lu\n", swap_bytes(12345678));
printf("Swapping 432: %lu\n", swap_bytes(432));
/* how about 432516? (err not) */
printf("Swapping 432: %lu\n", swap_bytes(432));
}
Now we run into an issue with numbers which are not divisible by 4... Do we pad them with zeros on the right or the left or the middle? err NOT REALLY.
In any event this entire approach is wrong because we're not swapping bytes anyhow, we're swapping characters.
Now what?
So you may be asking
what the heck is my assignment talking about?
Well numbers are represented as bytes in memory, and what the assignment is asking for is for you to get that representation and swap it.
So for example, if we took a number like 12345678 it's actually stored as some sequence of bytes (1 byte == 8 bits). So let's look at the normal math way of representing 12345678 (base 10) in bits (base 2) and bytes (base 8):
(12345678)10 = (101111000110000101001110)2
Splitting the binary bits into groups of 4 for visual ease gives:
(12345678)10 = (1011 1100 0110 0001 0100 1110)2
But 4 bits are equal to 1 hex number (0, 1, 2, 3... 9, A, B...F), so we can convert the bits into nibbles (4-bit hex numbers) easily:
(12345678)10 = 1011 | 1100 | 0110 | 0001 | 0100 | 1110
(12345678)10 = B | C | 6 | 1 | 4 | E
But each byte (8-bits) is two nibbles (4-bits) so if we squish this a bit:
(12345678)10 = (BC 61 4E)16
So 12345678 is actually representable in 3 bytes;
However CPUs have specific sizes for integers, usually these are multiples of 2 and divisible by 4. This is so because of a variety of reasons that are beyond the scope of this discussion, suffice it to say that you will get things like 16-bit, 32-bit, 64-bit, 128-bit etc... And most often the CPU of a particular bit-size (say a 64bit CPU) will be able to manipulate unsigned integers representable in that bit-size directly without having to store parts of the number in RAM.
Slight Digression
So let's say we have a 32-bit CPU, and somewhere at byte number α in RAM. The CPU could store the number 12345678 as:
> 00 BC 61 4E
> ↑ α ↑ α+1 ↑ α+2 ↑ α+3
(Figure 1)
Here the most significant part of the number, is sitting at the lowest memory address index α
Or the CPU could store it differently, where the least significant part of the number is sitting at the lowest memory.
> 4E 61 BC 00
> ↑ α ↑ α+1 ↑ α+2 ↑ α+3
(Figure 2)
The way a CPU stores a number is called Endianness (of the CPU). Where, if the most significant part is on the left then it's called Big-Endian CPU (Figure 1), or Little-Endian if it stores it as in (Figure 2)
Getting the correct answer (the wrong way)
Now that we have an idea of how things may be stored, let's try and pull this out still using sprintf.
We're going to use a couple of tricks here:
we'll convert the numbers to hexadecimal and then pad the number to 8 bytes
we'll use printf's (therefore sprintf) format string capability that if we want to use a variable to specify the width of an argument then we can use a * after the % sign like so:
printf("%*d", width, num);
If we set our format string to %0*x we get a hex number that's zero padded in output automatically, so:
sprintf(new, "%0*llx", sizeof(n), n);
Our program then becomes:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
unsigned long swap_bytes(unsigned long n) {
int i, l;
char new[64] = "";
sprintf(new, "%0*llx", sizeof(n), n);
printf("The number is: %s\n", new);
l = strlen(new);
for(i = 0; i < l; i+=4) {
char tmp[2];
tmp[0] = new[i+2]; /* get next two characters */
tmp[1] = new[i+3];
new[i+2] = new[i];
new[i+3] = new[i+1];
new[i] = tmp[0];
new[i+1] = tmp[1];
}
return strtoul(new, NULL, 16); /* convert new back */
}
/* testing swap byte */
int main() {
printf("size of unsigned long is %ld\n", sizeof(unsigned long));
printf("Swapping 12345678: %llx\n", swap_bytes(12345678));
/* how about 123456? */
printf("Swapping 123456: %llx\n", swap_bytes(123456));
printf("Swapping 123456: %llx\n", swap_bytes(98899));
}
The output would look something like:
size of unsigned long is 8
The number is: 00bc614e
Swapping 12345678: bc004e61
The number is: 0001e240
Swapping 123456: 10040e2
The number is: 00018253
Swapping 123456: 1005382
Obviously we can change our outputs by using %ld and print the base 10 versions of the numbers, rather than base 16 as is happening above. I'll leave that to you.
Now let's do it the right way
This is however rather terrible, since byte swapping can be done much faster without ever doing the integer to string and string to integer conversion.
Let's see how that's done:
The rather explicit way
Before we go on, just a bit on bit shifting in C:
If I have a number, say 6 (=1102) and I shift all the bits to the left by 1 I would get 12 (11002) (we simply shifted everything to the left adding zeros on the right as needed)
This is written in C as 6 << 1.
A right shift is similar and can be expressed in C with >> so if I have a number say 240 = (11110000)2 and I right-shift it 4 times I would get 15 = (1111)2 this is expressed as 240 >> 3
Now we have unsigned long integers which are (in my case at least) 64 bits long, or 8 bytes long.
Let's say my number is 12345678 which is (00 00 00 00 00 bc 61 4e)16 in hex at 8 bytes long. If I want to get the value of byte number 3 I can extract it by taking the number 0xFF (1111 1111) all bits of a byte set to 1 and left shifting it until i get to the byte 3 (so left shift 3*8 = 24 times) performing a bitwise and with the number and then right shifting the results to get rid of the zeros. This is what it looks like:
0xFF << (3 * 8) = 0xFF0000 & 0000 0000 00bc 614e = 0000 0000 00bc 0000
Now right shift:
0xFF0000 & 0000 0000 00bc 0000 >> (3 * 8) = bc
Another (better) way to do it would be to right shift first and then perform bitwise and with 0xFF to drop all higher bits:
0000 0000 00bc 614e >> 24 = 0000 0000 0000 00bc & 0xFF = bc
We will use the second way, and make a macro using #define now we can add the bytes back at the right location by right shifting each kth byte k+1 times and each k+1st byte k times.
Here is a sample implementation of this:
#define GET_BYTE(N, B) ((N >> (8 * (B))) & 0xFFUL)
unsigned long swap_bytes(unsigned long n)
{
unsigned long long rv = 0ULL;
int k;
printf("number is %016llx\n", n);
for(k =0 ; k < sizeof(n); k+=2) {
printf("swapping bytes %d[%016lx] and %d[%016lx]\n", k, GET_BYTE(n, k),
k+1, GET_BYTE(n, k+1));
rv += GET_BYTE(n, k) << 8*(k+1);
rv += GET_BYTE(n, k+1) << 8*k;
}
return rv;
}
/* testing swap byte */
int main() {
printf("size of unsigned long is: %ld\n", sizeof(unsigned long));
printf("Swapping 12345678: %llx\n", swap_bytes(12345678));
/* how about 123456? */
printf("Swapping 123456: %llx\n", swap_bytes(123456));
printf("Swapping 123456: %llx\n", swap_bytes(98899));
}
But this can be done so much more efficiently. I leave it here for now. We'll come back to using bit blitting and xor swapping later.
Update with GET_BYTE as a function instead of a macro:
#define GET_BYTE(N, B) ((N >> (8 * (B))) & 0xFFUL)
Just for fun we also use a shift operator for multiplying by 8. You can note that left shifting a number by 1 is like multiplying it by 2 (makes sense since in binary 2 is 10 and multiplying by 10 adds a zero to the end and therefore is the same as shifting something left by one space) So multiplying by 8 (1000)2 is like shifting something three spaces over or basically tacking on 3 zeros (overflows notwithstanding):
unsigned long __inline__ get_byte(const unsigned long n, const unsigned char idx) {
return ((n >> (idx << 3)) & 0xFFUL);
}
Now the really really fun and correct way to do this
Okay so a fast way to swap integers around is to realize that if we have two integers x, and y we can use properties of xor function to swap their values. The basic algorithm is this:
X := X XOR Y
Y := Y XOR X
X := X XOR Y
Now we know that a char is one byte in C. So we can force the compiler to treat the 8 byte integer as a sequence of 1-byte chars (hehe it's a bit of a mind bender considering everything I said about not doing it in sprintf) but this is different. You have to just think about it a bit.
We'll take the memory address of our integer, cast it to a char pointer (char *) and treat the result as an array of chars. Then we'll use the xor function property above to swap the two consecutive array values.
To do this I am going to use a macro (although we could use a function) but using a function will make the code uglier.
One thing you'll note is that there is the use of ?: in XORSWAP below. That's like an if-then-else in C but with expressions rather than statements, so basically (conditional_expression) ? (value_if_true) : (value_if_false) means if conditional_expression is non-zero the result will be value_if_true, otherwise it will be value_if_false. AND it's important not to xor a value with itself because you will always get 0 as a result and clobber the content. So we use the conditional to check if the addresses of the values we are changing are DIFFERENT from each other. If the addresses are the same (&a == &b) we simply return the value at the address (&a == &b) ? a : (otherwise_do_xor)
So let's do it:
#include <stdio.h>
/* this macro swaps any two non floating C values that are at
* DIFFERENT memory addresses. That's the entire &a == &b ? a : ... business
*/
#define XORSWAP(a, b) ((&(a) == &(b)) ? (a) : ((a)^=(b),(b)^=(a),(a)^=(b)))
unsigned long swap_bytes(const unsigned long n) {
unsigned long rv = n; /* we are not messing with original value */
int k;
for(k = 0; k < sizeof(rv); k+=2) {
/* swap k'th byte with k+1st byte */
XORSWAP(((char *)&rv)[k], ((char *)&rv)[k+1]);
}
return rv;
}
int main()
{
printf("swapped: %lx", swap_bytes(12345678));
return 0;
}
Here endeth the lesson. I hope that you will go through all the examples. If you have any more questions just ask in comments and I'll try to elaborate.
unsigned long swap_bytes(unsigned long n) {
char new[64];
sprintf(new, "%lu", n);
printf("Char array is now: %s\n", new);
}
You need to use %lu - long unsigned, for format in sprintf(), the compiler should also given you conversion lacks type warning because of this.
To get it to print you need to use %lu (for unsigned)
It doesn't seem like you attempted the swap, could I see your try?
I have a big char *str where the first 8 chars (which equals 64 bits if I'm not wrong), represents a bitmap. Is there any way to iterate through these 8 chars and see which bits are 0? I'm having alot of trouble understanding the concept of bits, as you can't "see" them in the code, so I can't think of any way to do this.
Imagine you have only one byte, a single char my_char. You can test for individual bits using bitwise operators and bit shifts.
unsigned char my_char = 0xAA;
int what_bit_i_am_testing = 0;
while (what_bit_i_am_testing < 8) {
if (my_char & 0x01) {
printf("bit %d is 1\n", what_bit_i_am_testing);
}
else {
printf("bit %d is 0\n", what_bit_i_am_testing);
}
what_bit_i_am_testing++;
my_char = my_char >> 1;
}
The part that must be new to you, is the >> operator. This operator will "insert a zero on the left and push every bit to the right, and the rightmost will be thrown away".
That was not a very technical description for a right bit shift of 1.
Here is a way to iterate over each of the set bits of an unsigned integer (use unsigned rather than signed integers for well-defined behaviour; unsigned of any width should be fine), one bit at a time.
Define the following macros:
#define LSBIT(X) ((X) & (-(X)))
#define CLEARLSBIT(X) ((X) & ((X) - 1))
Then you can use the following idiom to iterate over the set bits, LSbit first:
unsigned temp_bits;
unsigned one_bit;
temp_bits = some_value;
for ( ; temp_bits; temp_bits = CLEARLSBIT(temp_bits) ) {
one_bit = LSBIT(temp_bits);
/* Do something with one_bit */
}
I'm not sure whether this suits your needs. You said you want to check for 0 bits, rather than 1 bits — maybe you could bitwise-invert the initial value. Also for multi-byte values, you could put it in another for loop to process one byte/word at a time.
It's true for little-endian memory architecture:
const int cBitmapSize = 8;
const int cBitsCount = cBitmapSize * 8;
const unsigned char cBitmap[cBitmapSize] = /* some data */;
for(int n = 0; n < cBitsCount; n++)
{
unsigned char Mask = 1 << (n % 8);
if(cBitmap[n / 8] & Mask)
{
// if n'th bit is 1...
}
}
In the C language, chars are 8-bit wide bytes, and in general in computer science, data is organized around bytes as the fundamental unit.
In some cases, such as your problem, data is stored as boolean values in individual bits, so we need a way to determine whether a particular bit in a particular byte is on or off. There is already an SO solution for this explaining how to do bit manipulations in C.
To check a bit, the usual method is to AND it with the bit you want to check:
int isBitSet = bitmap & (1 << bit_position);
If the variable isBitSet is 0 after this operation, then the bit is not set. Any other value indicates that the bit is on.
For one char b you can simply iterate like this :
for (int i=0; i<8; i++) {
printf("This is the %d-th bit : %d\n",i,(b>>i)&1);
}
You can then iterate through the chars as needed.
What you should understand is that you cannot manipulate directly the bits, you can just use some arithmetic properties of number in base 2 to compute numbers that in some way represents some bits you want to know.
How does it work for example ? In a char there is 8 bits. A char can be see as a number written with 8 bits in base 2. If the number in b is b7b6b5b4b3b2b1b0 (each being a digit) then b>>i is b shifted to the right by i positions (in the left 0's are pushed). So, 10110111 >> 2 is 00101101, then the operation &1 isolate the last bit (bitwise and operator).
If you want to iterate through all char.
char *str = "MNO"; // M=01001101, N=01001110, O=01001111
int bit = 0;
for (int x = strlen(str)-1; x > -1; x--){ // Start from O, N, M
printf("Char %c \n", str[x]);
for(int y=0; y<8; y++){ // Iterate though every bit
// Shift bit the the right with y step and mask last position
if( str[x]>>y & 0b00000001 ){
printf("bit %d = 1\n", bit);
}else{
printf("bit %d = 0\n", bit);
}
bit++;
}
}
output
Char O
bit 0 = 1
bit 1 = 1
bit 2 = 1
bit 3 = 1
bit 4 = 0
bit 5 = 0
bit 6 = 1
bit 7 = 0
Char N
bit 8 = 0
bit 9 = 1
bit 10 = 1
...
I want to do left circular rotation for 64bit variables but my controller only operates in 8-bit, is it possible to do so?
Of course, it is possible to do so. It is just a question of how efficient it has to be.
Rotation by 1 bit can be implemented trivially, by shifting the bytes sequentially, inspecting the bit that "shifts out" from each 8-bit byte and carrying it over to the next 8-bit byte.
Rotation by n bits can be implemented in "dumb" fashion by performing a sequence of n 1-bit rotations.
A less "dumb" approach would be to perform circular reassignment (rotation) of the entire bytes n / 8 times, and then finishing it with n % 8 sequential 1-bit rotations. I.e. a 19 bit rotation = 2 full-byte rotations followed by 3 1-bit rotations.
You might also observe that in 64-bit word rotation left by 63 bits is the same as rotation right by 1 bit, meaning that if you have both left and right circular rotations at your disposal, then you should never have to rotate by more than 32 bits.
If the target word size is limited by 64 bits, then this might be sufficient already.
Assumes the 64-bit variable is little-endian. Modifies it in-place.
void rol(unsigned char a[8], int b){
unsigned char c[8];
unsigned char t, u;
int i, j;
/* Clamp b to [0, 63] */
b &= 63;
/* First rotate by 0 to 7 whole bytes (0 to 56 bits in multiples of 8) */
for(i=0, j=b/8;i<8;i++){
c[j] = a[i];
j++;
j &= 7;
}
/* Second, rotate by 0 to 7 bits, depending on what's left */
b &= 7;
if(b){/* Shift by 1 to 7 bits using bitwise ops into output */
for(i=0, j=7;i<8;i++){
u = c[i] << (b);
t = c[j] >> (8-b);
a[i] = u | t;
j++;
j &= 7;
}
}else{/* Shift by 0 bits = copy into output */
for(i=0;i<8;i++){
a[i] = c[i];
}
}
}
If i have an array of short unsigned ints.
Would shifting array[k+1] left by 8 bits, put 8 bits into the lower half of array[k+1]?
Or do they simply drop off as they have gone outside of the allocated space for the element?
They drop off. You can't affect the other bits this way. Try it:
#include <stdio.h>
void print_a (short * a)
{
int i;
for (i = 0; i < 3; i++)
printf ("%d:%X\n", i, a[i]);
}
int main ()
{
short a[3] = {1, -1, 3};
print_a (a);
a[1] <<= 8;
print_a (a);
return 0;
}
Output is
0:1
1:FFFFFFFF
2:3
0:1
1:FFFFFF00
2:3
They drop off the data type totally, not carrying over to the next array element.
If you want that sort of behavior, you have to code it yourself with something like (left shifting the entire array by four bits):
#include <stdio.h>
int main(void) {
int i;
unsigned short int a[4] = {0xdead,0x1234,0x5678,0xbeef};
// Output "before" variables.
for (i = 0; i < sizeof(a)/sizeof(*a); i++)
printf ("before %d: 0x%04x\n", i, a[i]);
printf ("\n");
// This left-shifts the array by left-shifting the current
// element and bringing in the top bit of the next element.
// It is in a loop for all but hte last element.
// Then it just left-shifts the last element (no more data
// to shift into that one).
for (i = 0; i < sizeof(a)/sizeof(*a)-1; i++)
a[i] = (a[i] << 8) | (a[i+1] >> 8);
a[i] = (a[i] << 8);
// Print the "after" variables.
for (i = 0; i < sizeof(a)/sizeof(*a); i++)
printf ("after %d: 0x%04x\n", i, a[i]);
return 0;
}
This outputs:
before 0: 0xdead
before 1: 0x1234
before 2: 0x5678
before 3: 0xbeef
after 0: 0xad12
after 1: 0x3456
after 2: 0x78be
after 3: 0xef00
The way to think about this is that in C (and for most programming languages) the implementation for array[k] << 8 involves loading array[k] into a register, shifting the register, and then storing the register back into array[k]. Thus array[k+1] will remain untouched.
As an example, foo.c:
unsigned short array[5];
void main() {
array[3] <<= 8;
}
Will generate the following instructions:
movzwl array+6(%rip), %eax
sall $8, %eax
movw %ax, array+6(%rip)
This loads array[3] into %eax, modifies it, and stores it back.
Shifting an unsigned int left by 8 bits will fill the lower 8 bits with zeros. The top 8 bits will be discarded, it doesn't matter that they are in an array.
Incidentally, whether 8 bits is half of an unsigned int depends on your system, but on 32-bit systems, 8 bits is typically a quarter of an unsigned int.
unsigned int x = 0x12345678;
// x is 0x12345678
x <<= 8;
// x is 0x34567800
Be aware that the C definition of the int data type does not specify how many bits it contains and is system dependent. An int was originally intended to be the "natural" word size of the processor, but this isn't always so and you could find int contains 16, 32, 64 or even some odd number like 24 bits.
The only thing you are guaranteed is an unsigned int can hold all the values between 0 and UINT_MAX inclusive, where UINT_MAX must be at least 65535 - so the int types must contain at least 16 bits to hold the required range of values.
So shifting an array of integer by 8 bits will change each int individually, but be aware that this shift will not necessarily be 'half of the array'