C Bitwise operations left shift and bitwise OR - c

Very short, I am having issues understanding the workings of this code, it is much more efficient then my 20 or so lines to get the same outcome. I understand how left shift is supposed to work and the bitwise Or but would appreciate a little guidance to understand how the two come together to make the line in the for loop work.
Code is meant to take in an array of bits(bits) of a given size(count) and return the integer value of the bits.
unsigned binary_array_to_numbers(const unsigned *bits, size_t count) {
unsigned res = 0;
for (size_t i = 0; i < count; i++)
res = res << 1 | bits[i];
return res;
}
EDIT: As requested, My newbie solution that still passed all tests: Added is a sample of possible assignment to bits[]
unsigned binary_array_to_numbers(const unsigned *bits, size_t count)
{
int i, j = 0;
unsigned add = 0;
for (i = count - 1; i >= 0; i--){
if(bits[i] == 1){
if(j >= 1){
j = j * 2;
add = add + j;
}
else{
j++;
add = add + j;
}
}
else {
if( j>= 1){
j = j * 2;
}
else{
j++;
}
}
}
return add;
}
void main(){
const unsigned bits[] = {0,1,1,0};
size_t count = sizeof(bits)/sizeof(bits[0]);
binary_array_to_numbers(bits, count);
}

a breakdown:
every left shift operation on a binary number effectively multiplies
it by 2 0111(7) << 1 = 1110(14)
consider rhubarbdog answer - the operation can be seen as two separate actions. first left-shift (multiply by two) and then OR with the current bit being reviewed
the PC does not distinguish between the value displayed and the binary
representation of the number
lets try and review a case in-which your input is:
bits = {0, 1, 0, 1};
count = 4;
unsigned binary_array_to_numbers(const unsigned *bits, size_t count) {
unsigned res = 0;
for (size_t i = 0; i < count; i++)
res = res << 1 // (a)
res = res | bits[i]; /* (b) according to rhubarbdog answer */
return res;
}
iteration 0:
- bits[i] = 0;
- (a) res = b0; (left shift of 0)
- (b) res = b0; (bitwise OR with 0)
iteration 1:
- bits[i] = 1;
- (a) res = b0; (left shift of 0)
- (b) res = b1; (bitwise OR with 1)
iteration 2:
- bits[i] = 0;
- (a) res = b10; (left shift of 1 - decimal value is 2)
- (b) res = b10; (bitwise OR with 0)
iteration 3:
- bits[i] = 1;
- (a) res = b100; (left shift of 1 - decimal value is 4)
- (b) res = b101; (bitwise OR with 1)
the final result for res is binary(101) and decimal(5) as one would expect
NOTE: the use of unsigned is a must since a signed value will be interpreted as a negative value if the MSB is 1
hope that helps...

consider them as 2 operations i'll re-write res= ... as 2 lines
res = res << 1
res = res | 1
The firs pass res gets set to 1, next time it's shifted *2 then because it's now even +1

Related

Set whole byte if first bit is 1

I'm trying to make program that will for for example 273(000100010001b) make to 4095(111111111111b).
Funny part is that my program works for first 2 iterations, for example 17(00010001b) correctly returns 255(11111111b) but every byte after that doesn't work. For instance 273(000100010001b) returns 61951(1111000111111111b) and I cant figure out why is that.
Here is my code
int x,temp, i;
int mask = 15;
scanf_s("%d", &x);
temp = x;
for(i=0;i<4;i++){
mask<<=i*4;
if((temp&1)==1){
x|=mask;
} else{
}
temp>>=4;
}
printf("%d", x);
The issue is the shift you perform on mask. You shift it by 0 on the 1st iteration, 4 on the 2nd, 12 (4 + 8) on the 3rd and so on.
Also, you don't apply the mask over all the bits of temp. I can't tell if you do this on purpose.
Here you are a fixed version:
int foo(int x) {
/* ^ I didn't know how to call such function */
static size_t const mask_size = 4;
unsigned mask = 15;
int temp = x;
for (unsigned i = 0; i < (CHAR_BIT * sizeof(int)) / mask_size; ++i) {
if ((temp & 1) == 1) {
x |= mask;
}
mask <<= mask_size;
temp >>= mask_size;
}
return x;
}
My results are:
in: 17(10) = 10001(2)
out: 255(10) = 11111111(2)
in: 273(10) = 100010001(2)
out: 4095(10) = 111111111111(2)
in: 4369(10) = 1000100010001(2)
out: 65535(10) = 1111111111111111(2)

random 4 digit number with non repeating digits in C

I'm trying to make a get_random_4digit function that generates a 4 digit number that has non-repeating digits ranging from 1-9 while only using ints, if, while and functions, so no arrays etc.
This is the code I have but it is not really working as intended, could anyone point me in the right direction?
int get_random_4digit() {
int d1 = rand() % 9 + 1;
int d2 = rand() % 9 + 1;
while (true) {
if (d1 != d2) {
int d3 = rand() % 9 + 1;
if (d3 != d1 || d3 != d2) {
int d4 = rand() % 9 + 1;
if (d4 != d1 || d4 != d2 || d4 != d3) {
random_4digit = (d1 * 1000) + (d2 * 100) + (d3 * 10) + d4;
break;
}
}
}
}
printf("Random 4digit = %d\n", random_4digit);
}
A KISS-approach could be this:
int getRandom4Digits() {
uint16_t acc = 0;
uint16_t used = 0;
for (int i = 0; i < 4; i++) {
int idx;
do {
idx = rand() % 9; // Not equidistributed but never mind...
} while (used & (1 << idx));
acc = acc * 10 + (idx + 1);
used |= (1 << idx);
}
return acc;
}
This looks terribly dumb at first. A quick analysis gives that this really isn't so bad, giving a number of calls to rand() to be about 4.9.
The expected number of inner loop steps [and corresponding calls to rand(), if we assume rand() % 9 to be i.i.d.] will be:
9/9 + 9/8 + 9/7 + 9/6 ~ 4.9107.
There are 9 possibilities for the first digit, 8 possibilities for the second digit, 7 possibilities for the third digit and 6 possibilities for the last digit. This works out to "9*8*7*6 = 3024 permutations".
Start by getting a random number from 0 to 3023. Let's call that P. To do this without causing a biased distribution use something like do { P = rand() & 0xFFF; } while(P >= 3024);.
Note: If you don't care about uniform distribution you could just do P = rand() % 3024;. In this case lower values of P will be more likely because RAND_MAX doesn't divide by 3024 nicely.
The first digit has 9 possibilities, so do d1 = P % 9 + 1; P = P / 9;.
The second digit has 8 possibilities, so do d2 = P % 8 + 1; P = P / 8;.
The third digit has 7 possibilities, so do d3 = P % 7 + 1; P = P / 7;.
For the last digit you can just do d4 = P + 1; because we know P can't be too high.
Next; convert "possibility" into a digit. For d1 you do nothing. For d2 you need to increase it if it's greater than or equal to d1, like if(d2 >= d1) d2++;. Do the same for d3 and d4 (comparing against all previous digits).
The final code will be something like:
int get_random_4digit() {
int P, d1, d2, d3, d4;
do {
P = rand() & 0xFFF;
} while(P >= 3024);
d1 = P % 9 + 1; P = P / 9;
d2 = P % 8 + 1; P = P / 8;
d3 = P % 7 + 1; P = P / 7;
d4 = P + 1;
if(d2 >= d1) d2++;
if(d3 >= d1) d3++;
if(d3 >= d2) d3++;
if(d4 >= d1) d4++;
if(d4 >= d2) d4++;
if(d4 >= d3) d4++;
return d1*1000 + d2*100 + d3*10 + d4;
}
You could start with an integer number, 0x123456789, and pick random nibbles from it (the 4 bits that makes up one of the digits in the hex value). When a nibble has been selected, remove it from the number and continue picking from those left.
This makes exactly 4 calls to rand() and has no if or other conditions (other than the loop condition).
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int get_random_4digit() {
uint64_t bits = 0x123456789; // nibbles
int res = 0;
// pick random nibbles
for(unsigned last = 9 - 1; last > 9 - 1 - 4; --last) {
unsigned lsh = last * 4; // shift last nibble
unsigned sel = (rand() % (last + 1)) * 4; // shift for random nibble
// multiply with 10 and add the selected nibble
res = res * 10 + ((bits & (0xFULL << sel)) >> sel);
// move the last unselected nibble right to where the selected
// nibble was:
bits = (bits & ~(0xFULL << sel)) |
((bits & (0xFULL << lsh)) >> (lsh - sel));
}
return res;
}
Demo
Another variant could be to use the same value, 0x123456789, and do a Fisher-Yates shuffle on the nibbles. When the shuffle is done, return the 4 lowest nibbles. This is more expensive since it randomizes the order of all 9 nibbles - but it makes it easy if you want to select an arbitrary amount of them afterwards.
Example:
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <time.h>
uint16_t get_random_4digit() {
uint64_t bits = 0x123456789; // nibbles
// shuffle the nibbles
for(unsigned idx = 9 - 1; idx > 0; --idx) {
unsigned ish = idx * 4; // index shift
// shift for random nibble to swap with `idx`
unsigned swp = (rand() % (idx + 1)) * 4;
// extract the selected nibbles
uint64_t a = (bits & (0xFULL << ish)) >> ish;
uint64_t b = (bits & (0xFULL << swp)) >> swp;
// swap them
bits &= ~((0xFULL << ish) | (0xFULL << swp));
bits |= (a << swp) | (b << ish);
}
return bits & 0xFFFF; // return the 4 lowest nibbles
}
The bit manipulation can probably be optimized - but I wrote it like I thought it so it's probably better for readability to leave it as-is
You can then print the value as a hex value to get the output you want - or extract the 4 nibbles and convert it for decimal output.
int main() {
srand(time(NULL));
uint16_t res = get_random_4digit();
// print directly as hex:
printf("%X\n", res);
// or extract the nibbles and multiply to get decimal result - same output:
uint16_t a = (res >> 12) & 0xF;
uint16_t b = (res >> 8) & 0xF;
uint16_t c = (res >> 4) & 0xF;
uint16_t d = (res >> 0) & 0xF;
uint16_t dec = a * 1000 + b * 100 + c * 10 + d;
printf("%d\n", dec);
}
Demo
You should keep generating digits until distinct one found:
int get_random_4digit() {
int random_4digit = 0;
/* We must have 4 digits number - at least 1234 */
while (random_4digit < 1000) {
int digit = rand() % 9 + 1;
/* check if generated digit is not in the result */
for (int number = random_4digit; number > 0; number /= 10)
if (number % 10 == digit) {
digit = 0; /* digit has been found, we'll try once more */
break;
}
if (digit > 0) /* unique digit generated, we add it to result */
random_4digit = random_4digit * 10 + digit;
}
return random_4digit;
}
Please, fiddle youself
One way to do this is to create an array with all 9 digits, pick a random one and remove it from the list.
Something like this:
uint_fast8_t digits[]={1,2,3,4,5,6,7,8,9}; //only 1-9 are allowed, 0 is not allowed
uint_fast8_t left=4; //How many digits are left to create
unsigned result=0; //Store the 4-digit number here
while(left--)
{
uint_fast8_t digit=getRand(9-4+left); //pick a random index
result=result*10+digits[digit];
//Move all digits above the selcted one 1 index down.
//This removes the picked digit from the array.
while(digit<8)
{
digits[digit]=digits[digit+1];
digit++;
}
}
You said you need a solution without arrays. Luckily, we can store up to 16 4 bit numbers in a single uint64_t. Here is an example that uses a uint64_t to store the digit list so that no array is needed.
#include <stdint.h>
#include <inttypes.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
unsigned getRand(unsigned max)
{
return rand()%(max+1);
}
//Creates a uint64_t that is used as an array.
//Use no more than 16 values and don't use values >0x0F
//The last argument will have index 0
uint64_t fakeArrayCreate(uint_fast8_t count, ...)
{
uint64_t result=0;
va_list args;
va_start (args, count);
while(count--)
{
result=(result<<4) | va_arg(args,int);
}
return result;
}
uint_fast8_t fakeArrayGet(uint64_t array, uint_fast8_t index)
{
return array>>(4*index)&0x0F;
}
uint64_t fakeArraySet(uint64_t array, uint_fast8_t index, uint_fast8_t value)
{
array = array & ~((uint64_t)0x0F<<(4*index));
array = array | ((uint64_t)value<<(4*index));
return array;
}
unsigned getRandomDigits(void)
{
uint64_t digits = fakeArrayCreate(9,9,8,7,6,5,4,3,2,1);
uint_fast8_t left=4;
unsigned result=0;
while(left--)
{
uint_fast8_t digit=getRand(9-4+left);
result=result*10+fakeArrayGet(digits,digit);
//Move all digits above the selcted one 1 index down.
//This removes the picked digit from the array.
while(digit<8)
{
digits=fakeArraySet(digits,digit,fakeArrayGet(digits,digit+1));
digit++;
}
}
return result;
}
//Test our function
int main(int argc, char **argv)
{
srand(atoi(argv[1]));
printf("%u\n",getRandomDigits());
}
You could use a partial Fisher-Yates shuffle on an array of 9 digits, stopping after 4 digits:
// Return random integer from 0 to n-1
// (for n in range 1 to RAND_MAX+1u).
int get_random_int(unsigned int n) {
unsigned int x = (RAND_MAX + 1u) / n;
unsigned int limit = x * n;
int s;
do {
s = rand();
} while (s >= limit);
return s / x;
}
// Return random 4-digit number from 1234 to 9876 with no
// duplicate digits and no 0 digit.
int get_random_4digit(void) {
char possible[9] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
int result = 0;
int i;
// Uses partial Fisher-Yates shuffle.
for (i = 0; i < 4; i++) {
// Get random position rand_pos from remaining possibilities i to 8
// (positions before i contain previous selected digits).
int rand_pos = i + get_random_int(9 - i);
// Select digit from position rand_pos.
char digit = possible[rand_pos];
// Exchange digits at positions i and rand_pos.
possible[rand_pos] = possible[i];
possible[i] = digit; // not really needed
// Put selected digit into result.
result = result * 10 + digit;
}
return result;
}
EDIT: I forgot the requirement "while only using int's, if, while and functions, so no arrays etc.", so feel free to ignore this answer!
If normal C integer types are allowed including long long int, the get_random_4digit() function above can be replaced with the following to satisfy the requirement:
// Return random 4-digit number from 1234 to 9876 with no
// duplicate digits and no 0 digit.
int get_random_4digit(void) {
long long int possible = 0x123456789; // 4 bits per digit
int result = 0;
int i;
// Uses partial Fisher-Yates shuffle.
i = 0;
while (i < 4) {
// Determine random position rand_pos in remaining possibilities 0 to 8-i.
int rand_pos = get_random_int(9 - i);
// Select digit from position rand_pos.
int digit = (possible >> (4 * rand_pos)) & 0xF;
// Replace digit at position rand_pos with digit at position 0.
possible ^= ((possible ^ digit) & 0xF) << (4 * rand_pos);
// Shift remaining possible digits down one position.
possible >>= 4;
// Put selected digit into result.
result = result * 10 + digit;
i++;
}
return result;
}
There are multiple answers to this question already, but none of them seem to fit the requirement only using ints, if, while and functions. Here is a modified version of Pelle Evensen's simple solution:
#include <stdlib.h>
int get_random_4digit(void) {
int acc = 0, used = 0, i = 0;
while (i < 4) {
int idx = rand() % 9; // Not strictly uniform but never mind...
if (!(used & (1 << idx))) {
acc = acc * 10 + idx + 1;
used |= 1 << idx;
i++;
}
}
return acc;
}

Converting negative binary to decimal

I have been trying for the past several hours to convert a negative binary with the first bit as 1 to a decimal. The twos complement conversion seems to be impossible but I believe there has to be some easier way to do this because this is just the beginning of a beginner class in C.
int power;
int count = 0;
int length = strlen(value);
int result = 0;
int negResult = 0;
int i = length - 1;
int j;
if (value[0] == '1') {
for (; i >= 0; i--) {
if (value[i] == '1')
result += 1;
result << 1;
}
printf("%d\n", result);
result = ~result;
result += 1;
printf("%d\n", result);
for (j = 8; j > 0; j--) {
if (result << (8-j) == 1) {
power = (int) pow(2,count);
negResult += power;
}
count++;
}
printf("-%d\n", negResult);
}
else {
for (; i >= 0; i--) {
if (value[i] == '1') {
power = (int) pow(2,count);
result = result + power;
}
count++;
}
printf("%d\n", result);
}
}
I pass it:
binary_to_decimal("10011011");
and I get 5 then -5 and then -0 for each printf.
I did not include the code that actually converts it to a decimal since for positive binaries it works fine and I believe once the twos complement works it should work for negative binaries as well.
You may not be clear about what temp += '0' is doing. It's not making a string, rather it's offsetting an uninitialized pointer and is the cause of the segfault when you come to actually use it as in temp2[j]. To work with strings like I think you're wanting to, check out strcat().
I would junk this and start over. Don't manipulate chars in strings, just convert the string to a binary. Write some code to walk the input string from the first character towards the last. Keep a result integer for your answer, initially 0. As you walk through the string, shift result << 1, and then if you see a char '1', add a number 1 to result. If you see a '0' in the string don't add anything, but in either case do the left shift first.
This will get you a binary of however many bits you have. For negative numbers (topmost (first) bit = '1') you will need to sign extend by OR-ing '1' into all the bits above the sign bit, bitwise-invert the result and add 1. Check this on paper to see how it works, and be aware the input string can't be too long. Good luck with the class.
int length = strlen(value);
unsigned int result = 0;
unsigned int signExtend;
unsigned int negResult = 0;
// assemble incoming chars as bits in an unsigned int
for (int i=0;i<length;i++) {
result = result << 1;
if (value[i] == '1')
result += 1;
}
printf("0x%x, %d\n", result, result); // see it is a hex number and a decimal
// if negative, convert to positive number
if (value[0] == '1') {
// first, sign-extend
signExtend = (1 << (length-1));
signExtend -= 1;
signExtend = ~signExtend;
result |= signExtend;
printf("signExtend mask = 0x%x, sign-extended number = %x\n", signExtend, result);
// then, two's complement
negResult = ~result;
negResult += 1;
// show the result with the '-' sign explicitly added:
printf("result is -%d\n", negResult);
// but actually, once you have sign extended,
// you can cast the result as signed and just print it:
printf("result as signed int = %d\n", (int)result);
} else {
// positive result, just print it
printf("result is %d\n", result);
}
here's how I would do it
int btd(char *str)
{
int i , l = strlen(str) , neg = 0;
int res = 0;
for(i = 0 ; i < l ; i++)
{
if(!i)
{
if(str[i] == '1');
neg++;
continue;
}
if(str[i] == '1')
{
res <<= 1;
res |= 1;
}
else
res <<= 1;
}
if(neg)
res *= -1;
return res;
}

My B64 Encoder doesn't work

Hi I made a base 64 encoder and some of it works but not 100% and its driving me crazy why it won't work.
int b64_encode(FILE *in, FILE *out)
{
do
{
char outbuf [76+3];// room for null \n \r
char inbuf [57];//ratio 4ascii/3chars
for(int i=0; i < sizeof(inbuf);i++)
inbuf[i]=0;
int i1;
int i2;
int i3;
int o1;
int o2;
int o3;
int o4;
int combine;
size_t read_cnt;
read_cnt = fread(inbuf,1,sizeof(inbuf),in);
for (int i = 0; i < read_cnt; i=i+3)
{
i3 = inbuf[i+2];
i2 =inbuf[i+1] << 8;
i1 = inbuf[i] << 16;
combine = i1+i2+i3;
o4 = (combine & 0x3f);
combine = combine >> 6;
o3 = (combine & 0x3f);
combine = combine >> 6;
o2 = (combine & 0x3f);
combine = combine >> 6;
o1 = (combine & 0x3f);
combine = combine >> 6;
outbuf[i*4/3]=ENCODE[o1];
outbuf[i*4/3+1]=ENCODE[o2];
outbuf[i*4/3+2]=ENCODE[o3];
outbuf[i*4/3+3]=ENCODE[o4];
}
for(int i=0; i < sizeof(outbuf);i++)
{
if(outbuf[i]==0)
{
outbuf[i+2]='\0';
outbuf[i]='\r';
outbuf[i+1]='\n';
i=sizeof(outbuf);
}
}
//padding
if((read_cnt%3)==2)
{
int t= (int)read_cnt;
int temp= ((t/3)+1)*4;
outbuf[temp-1] = '=';
}
if((read_cnt%3)==1)
{
int t= (int)read_cnt;
int temp= ((t/3)+1)*4;
outbuf[temp-1] = '=';
outbuf[temp-2] = '=';
}
//for(int i=0; i < sizeof((read_cnt)*4/3);i++)
printf("%s",outbuf);
}
while(feof(in)==0);
return 0;
}
When I type the example I get on wikipedia of 'Man is distinguished, not only by his reason, but by this singular passion from
other animals, which is a lust of the mind, that by a perseverance of delight
in the continued and indefatigable generation of knowledge, exceeds the short
vehemence of any carnal pleasure.', it should be 'TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlz
IHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2Yg
dGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGlu
dWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRo
ZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4='
but I get
'TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlz
IHNpbmd1bGFyIHBhc3Npb24gZnJvbQpvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2Yg
dGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodAppbiB0aGUgY29udGlu
dWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRo
ZSBzaG9ydAp2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4KZGdlLCBleGNlZWRzIHRo'
which is the same thing but with this little extra on the end 'KZGdlLCBleGNlZWRzIHRo'. Can someone please help me understand where this extra code is coming from ? thanks so much
You have couple of problems in you code.
You are reading past your data buffer.
i3 = inbuf[i+2];
i2 = inbuf[i+1] << 8;
You really should add checks, for not getting values outside [0..read_cnt-1]
You are using shift on signed integers. That would give you interesting results when working with non-ASCII text or binary data
i3 = (unsigned char*)inbuf[i+2];
i2 = (unsigned char*)inbuf[i+1] << 8;
i1 = (unsigned char*)inbuf[i] << 16;
Reason is that char is a signed type, which is expanded to int with all high bits set when the char has bit 7 set (exceeds 0x7f)
Also combine should be unsigned.
You are not terminating outbuf after encoding loop.
You are trying to find 0, but it is not there...
Instead of multiple paddings, you might try the following:
int pos = read_cnt * 4;
switch (read_cnt % 3)
{
case 1: outbuf[pos++] = '=';
case 2: outbuf[pos++] = '=';
case 0: outbuf[pos++] = '\r';
outbuf[pos++] = '\n';
outbuf[pos] = 0;
}
read_cnt should be ssize_t
Try...
for (int i = 0; i < read_cnt - 2; i=i+3)
... instead of...
for (int i = 0; i < read_cnt; i=i+3)
I tried to understand what exactly you are trying to do by checking the Wikipedia article, but I cannot say that I have understood the whole thing correctly.

Bitwise shifting array of char's

I have got an array of chars that I'm trying to bitwise shift right >>, then & with another array. I think I have got the wrong idea of how to do this.
I thought, even though it was an array of chars just stating my_array >>= 1 would shift everything but I am getting an error: "error: invalid operands to binary >> (have ‘char[8]’ and ‘int’)"
The bitwise comparision I am trying to do is with a similar size array initiated to all "0's"...for that I'm getting: "error: invalid operands to binary & (have ‘char *’ and ‘char *’)"
Do I need to convert these array's into something else before I can shift and compare?
Sorry, I was not super clear... All great advice up to this point and I think I am realizing more that there is no super easy way to do this. More specifically, what I am trying to do is shift the bits of the WHOLE char array right 1, adding the bit shifted off the right back to the left most side of the array, do the bitwise compare with another array of same size.
Technically the compare doesn't have to be array with array... I just need the bits. Would it be easier to convert the array's to something else before trying to do the shifts/comparisons?
You have to shift and compare elementwise.
for(i = 0; i < len; ++i)
array[i] >>= 3;
for example. If you want to move the bits shifted out of one element to the next, it's more complicated, say you're shifting right, then
unsigned char bits1 = 0, bits2 = 0;
for(i = len-1; i >= 0; --i) {
bits2 = array[i] & 0x07;
array[i] >>= 3;
array[i] |= bits1 << 5;
bits1 = bits2;
}
traversing the array in the other direction because you need the bits from the next higher slot.
You'll have to shift the entries in the array one by one. (And if you want to compare two of these, you'll need to do it element by element.)
If you were hoping that bits shifted off each char would get shifted into the next one, you'll need to take care of that manually too.
If you are wanting that shift-into-the-next-byte behaviour, and don't mind making your code nasty and nonportable and bug-prone, you might be able to take a pointer to the array, cast it to something like unsigned long long *, dereference it and shift the resulting integer, and store it back again.
BUT if that's the behaviour you want then you should be using an integer instead of a char[8] to begin with.
(If you could say more about what you're actually aiming to achieve, then more helpful answers may be possible.)
If you want to perform operations such as shifting / OR / XOR / AND / etc.. on arrays, you should perform it in a loop, you cannot perform it directly on the array.
/** Shift an array right.
* #param ar The array to shift.
* #param size The number of array elements.
* #param shift The number of bits to shift.
*/
void shift_right(unsigned char *ar, int size, int shift)
{
int carry = 0; // Clear the initial carry bit.
while (shift--) { // For each bit to shift ...
for (int i = size - 1; i >= 0; --i) { // For each element of the array from high to low ...
int next = (ar[i] & 1) ? 0x80 : 0; // ... if the low bit is set, set the carry bit.
ar[i] = carry | (ar[i] >> 1); // Shift the element one bit left and addthe old carry.
carry = next; // Remember the old carry for next time.
}
}
}
You can shift only members of that arrays, a char (or an int). You can't shift an entire array. Shifting my_array tries to perform a shift operation on an array type (or a pointer to char) which is impossible. Do this instead:
for (i = 0; i < size; i++) {
my_array[i] >>= 1;
}
Also you must be careful with chars because they are usually signed, and a char containing a negative value will bring '1' from the left instead of zeros. So you better use unsigned chars.
EDIT:
The code above is simplistic. If you intended to shift right the array as a whole, not just each byte on its own, then you need to "manually" copy each LSB to the MSB of the byte to its right. Take a loop at the answer of Richard Pennington.
/**
* shift a number of bits to the right
*
* #param SRC the array to shift
* #param len the length of the array
* #param shift the number of consecutive bits to shift
*
*/
static void shift_bits_right(uint8_t SRC[], uint16_t len, uint32_t shift) {
uint32_t i = 0;
uint8_t start = shift / 8;
uint8_t rest = shift % 8;
uint8_t previous = 0;
for(i = 0; i < len; i++) {
if(start <= i) {
previous = SRC[i - start];
}
uint8_t value = (previous << (8 - rest)) | SRC[i + start] >> rest;
SRC[i + start] = value;
}
}
I know this is old topic but i was not satisfied with the answers available, here is something i wrote recently which allows you to specify the amount of bits you can shift by and also there is simple XOR encryption in it.
//https://github.com/ashvin-bhuttoo/CryptoTest/blob/master/CryptoTest/Crypto.cpp
//CRYPTO CONFIGURATION PARAMETERS
#define BIT_SHIFT 3
#define XOR_KEY 0x3C
#define ENABLE_XOR_VARIANCE true
////////////////////////////////
int get_rs_mask(int shift)
{
switch (shift)
{
case 0:
return 0x00;
case 1:
return 0x01;
case 2:
return 0x03;
case 3:
return 0x07;
case 4:
return 0x0F;
case 5:
return 0x1F;
case 6:
return 0x3F;
case 7:
return 0x7F;
default:
throw "get_rs_mask -> Error, shift argument outside legal range 0-7";
}
}
void shift_right(char* buf, int msg_len, int shift)
{
unsigned char tmp = 0x00, tmp2 = 0x00;
for (int k = 0; k <= msg_len; k++)
{
if (k == 0)
{
tmp = buf[k];
buf[k] >>= shift;
}
else
{
tmp2 = buf[k];
buf[k] >>= shift;
buf[k] |= ((tmp & get_rs_mask(shift)) << (8 - shift));
if (k != msg_len)
tmp = tmp2;
}
}
}
int get_ls_mask(int shift)
{
switch (shift)
{
case 0:
return 0x00;
case 1:
return 0x80;
case 2:
return 0xC0;
case 3:
return 0xE0;
case 4:
return 0xF0;
case 5:
return 0xF8;
case 6:
return 0xFC;
case 7:
return 0xFE;
default:
throw "get_ls_mask -> Error, shift argument outside legal range 0-7";
}
}
void shift_left(char* buf, int msg_len, int shift)
{
char tmp = 0x00, tmp2 = 0x00;
for (int k = msg_len; k >= 0; k--)
{
if (k == msg_len)
{
tmp = buf[k];
buf[k] <<= shift;
}
else
{
tmp2 = buf[k];
buf[k] <<= shift;
buf[k] |= ((tmp & get_ls_mask(shift)) >> (8 - shift));
tmp = tmp2;
}
}
}
void crypt(char* buf, int msg_len, bool decrypt = false)
{
if (!decrypt)
{
shift_right(buf, msg_len, BIT_SHIFT);
for (int k = 0; k < msg_len; k++)
{
buf[k] = buf[k] ^ XOR_KEY ^ k * (ENABLE_XOR_VARIANCE ? 2 : 0);
}
buf[msg_len] = '\0';
}
else
{
for (int k = 0; k < msg_len; k++)
{
buf[k] = buf[k] ^ XOR_KEY ^ k * (ENABLE_XOR_VARIANCE ? 2 : 0);
}
shift_left(buf, (msg_len)-1, BIT_SHIFT);
}
}
/**
* Shift a number of bits to the right
*
* #param array The array to shift
* #param len The length of the array
* #param shift The number of consecutive bits to shift. To the right if shift is positif.
*
*/
static void shift_bits_right(uint8_t *array, int len, int shift) {
uint8_t macro_shift = shift / 8;
shift = shift % 8;
uint8_t array_out[len];
memset(array_out, 0, len);
for(int i = 0; i < len; i++) {
if(i+macro_shift < len)
array_out[i+macro_shift] += array[i]>>shift;
if(i+macro_shift+1 < len)
array_out[i+macro_shift+1] += array[i]<<(8-shift);
}
memcpy(array, array_out, len);
}
For everyone who is looking for a code snippet to (logically) shift right a byte array that actually works:
template<size_t N> void shift_right(array<uint8_t, N>& arr, uint64_t bits)
{
int64_t num_bytes = bits / 8;
int64_t num_bits = bits % 8;
for(int64_t i = N-1; i >= 0; i--)
{
int64_t i_from = i - num_bytes;
int64_t i_from_minus_one = i - num_bytes - 1;
uint8_t v_from = i_from < 0 ? 0 : arr[i_from];
uint8_t v_from_minus_one = i_from_minus_one < 0 ? 0 : arr[i_from_minus_one];
arr[i] = v_from >> num_bits | v_from_minus_one << (8 - num_bits);
}
}

Resources