0

What I am asking is if it is possible to join all bits in 2 different numbers.

A pseudo-code example:

bytes=array(0x04, 0x3F);

//place bitwise black magic here

print 0x043F;

Another example:

bytes=array(0xFF, 0xFFFF);

//place bitwise black magic here

print 0xFFFFFF;

Yet another example:

bytes=array(0x34F3, 0x54FD);

//place bitwise black magic here

print 0x34F354FD;

I want to restrict this to only and only bitwise operators (>>, <<, |, ^, ~ and &).

This should work at least in PHP and Javascript.

Is this possible in ANY way?

If I'm not being clear, please ask your doubts in a comment.

Ismael Miguel
  • 4,185
  • 1
  • 31
  • 42

3 Answers3

1

If I understand your question correctly,
This should be the answer in php:

    $temp = $your_first_value << strlen(dechex($the_length_of_the_second_value_in_hex))
    $result = $temp | $your_second_value
    print dechex($result)

Update: instead of + use the | operator

Ehsan
  • 4,334
  • 7
  • 39
  • 59
  • That does use an addition (`+´) operator. I had specified only and only bitwise operators. I made an edit to highlight that piece and listed the operators to use. But you do present a really nice solution! – Ismael Miguel Nov 19 '14 at 11:29
  • Actually, the `$temp = $your_first_value << $the_length_of_the_second_value_in_hex` bit would be `$temp = $your_first_value << 8`. Like this, it works for arbitrary integers (below 32 bits). – Ismael Miguel Nov 19 '14 at 11:38
  • Your answer is now exactly like uliwitness's answer, which has the same problem. This works great if `$your_second_value` is restricted between 0 and 255. That assumes only 1 byte. What I'm asking is arbitrary numbers (Quoting: `What I am asking is if it is possible to join all bits in 2 different` **`numbers`**). – Ismael Miguel Nov 19 '14 at 11:45
  • I have added 2 more examples of what I pretend, to clarify what I expect. – Ismael Miguel Nov 19 '14 at 11:48
  • So the only problem is resolving the length of the second value in hex,and then everything should be fine right? – Ehsan Nov 19 '14 at 11:48
  • I updated my answer in a way the you get the shifting is based on the second number length in hex – Ehsan Nov 19 '14 at 11:53
  • It does work beautifully. However, I was expecting a __pure__ bitwise answer. – Ismael Miguel Nov 19 '14 at 11:54
  • Well, I think if we could have solve the problem of finding the length of the second number in hex, you would have had your answer :) – Ehsan Nov 19 '14 at 12:04
  • And I don't know the answer now, but that was all the help that I could have gave you. Best of lucks – Ehsan Nov 19 '14 at 12:05
  • I am ahead of you. You did helped a lot more than I though it was possible to achieve. Thank you. If I find a solution, I will post here. – Ismael Miguel Nov 19 '14 at 12:08
  • So if you find it helpful, please consider giving a vote up ;) thanks :) – Ehsan Nov 19 '14 at 12:11
  • BTW, this is how you count the number of 1's in a binary number with binary operations, http://en.wikipedia.org/wiki/Hamming_weight if you can also find the number of zeros with bitwise operations. It should be done! – Ehsan Nov 19 '14 at 12:15
  • I have found this ugly solution: `(v1>>8)?(v1>>16)?(v1>>24)?32:24:16:8;`. Do you think this will work? – Ismael Miguel Nov 19 '14 at 12:16
  • This is how I got it working on PHP (still a language-agnostic solution): `(v2>>8?(v2>>16?(v2>>24?32:24):16):8)`. (it should be v2 on the other comment). – Ismael Miguel Nov 19 '14 at 12:21
  • Well, this is not bad, but what if a number has more than 32 bits. just to make sure I recommend doing something like this strlen(decbin(PHP_INT_MAX)) Then you can make sure whether the max number of bits is not exceeding 32 bits. – Ehsan Nov 19 '14 at 12:22
  • Using my ugly code (something like `(v2>>8?(v2>>16?(v2>>24?(v2>>32?(v2>>40?(v2>>48?(v2>>56?64:56):48):40):32):24):16):8)`) would solve for other languages. Be careful with unsigned numbers (PHP): `0xffffffffffffffe` produces 64 while `0xfffffffffffffff` gives 8 (which is correct, but useless). On those languages, `0xfffffffffffffff` is -1. This is the only number to produce invalid results. – Ismael Miguel Nov 19 '14 at 12:36
1

This problem hinges completely on being able to determine the position of the leftmost 1 in an integer. One way to do that is by "smearing the bits right" and then counting the 1's:

Smearing to the right:

int smearright(int x) {
    x |= x >> 1;
    x |= x >> 2;
    x |= x >> 4;
    x |= x >> 8;
    x |= x >> 16;
    return x;
}

Easy, only bitwise operators there. Counting the bits however involves some sort of addition:

int popcnt(int x) {
    x = add(x & 0x55555555, (x >> 1) & 0x55555555);
    x = add(x & 0x33333333, (x >> 2) & 0x33333333);
    x = add(x & 0x0f0f0f0f, (x >> 4) & 0x0f0f0f0f);
    x = add(x & 0x00ff00ff, (x >> 8) & 0x00ff00ff);
    x = add(x & 0xffff, (x >> 16) & 0xffff);
    return x;
}

But that's OK, add can be implemented as

int add(int x, int y) {
    int p = x ^ y;
    int g = x & y;
    g |= p & (g << 1);
    p &= p << 1;
    g |= p & (g << 2);
    p &= p << 2;
    g |= p & (g << 4);
    p &= p << 4;    
    g |= p & (g << 8);
    p &= p << 8;
    g |= p & (g << 16);
    return x ^ y ^ (g << 1);
}

Putting it together:

join = (left << popcnt(smearright(right))) | right;

It's obviously much easier if you had addition (no add function), perhaps surprisingly though, it's even simpler than that with multiplication:

join = (left * (smearright(right) + 1)) | right;

No more popcnt at all!

Implementing multiplication in terms of bitwise operators wouldn't help, that's much worse and I'm not sure you can even do it with the listed operators (unless the right shift is an arithmetic shift, but then it's still a terrible thing involving 32 additions each of which are function themselves).

There were no "sneaky tricks" in this answer, such as using conditions that implicitly test for equality with zero ("hidden" != 0 in an if, ?:, while etc), and the control flow is actually completely linear (function calls are just there to prevent repeated code, everything can be inlined).


Here's an alternative. Instead of taking the popcnt, do a weird variable shift:

int shift_by_mask(int x, int mask) {
    x <<= mask & 1;
    mask >>= 1;
    x <<= mask & 1;
    mask >>= 1;
    x <<= mask & 1;
    mask >>= 1;
    x <<= mask & 1;
    mask >>= 1;
    x <<= mask & 1;
    mask >>= 1;
    x <<= mask & 1;
    mask >>= 1;
    x <<= mask & 1;
    mask >>= 1;
    x <<= mask & 1;
    mask >>= 1;
    x <<= mask & 1;
    mask >>= 1;
    x <<= mask & 1;
    mask >>= 1;
    x <<= mask & 1;
    mask >>= 1;
    x <<= mask & 1;
    mask >>= 1;
    x <<= mask & 1;
    mask >>= 1;
    x <<= mask & 1;
    mask >>= 1;
    x <<= mask & 1;
    mask >>= 1;
    x <<= mask & 1;
    mask >>= 1;
    x <<= mask & 1;
    mask >>= 1;
    x <<= mask & 1;
    mask >>= 1;
    x <<= mask & 1;
    mask >>= 1;
    x <<= mask & 1;
    mask >>= 1;
    x <<= mask & 1;
    mask >>= 1;
    x <<= mask & 1;
    mask >>= 1;
    x <<= mask & 1;
    mask >>= 1;
    x <<= mask & 1;
    mask >>= 1;
    x <<= mask & 1;
    mask >>= 1;
    x <<= mask & 1;
    mask >>= 1;
    x <<= mask & 1;
    mask >>= 1;
    x <<= mask & 1;
    mask >>= 1;
    x <<= mask & 1;
    mask >>= 1;
    x <<= mask & 1;
    mask >>= 1;
    x <<= mask & 1;
    mask >>= 1;
    x <<= mask & 1;
    return x;
}

Ok that doesn't make me happy, but here's how you'd use it:

join = shift_by_mask(left, smearright(right)) | right;
harold
  • 61,398
  • 6
  • 86
  • 164
  • This is a language-agnostic question, and you seem to give a c-only answer. Javascript and PHP won't work with that. PHP doesn't even have unsigned numbers! – Ismael Miguel Nov 19 '14 at 12:53
  • @IsmaelMiguel I did that to make the right shifts logical shifts, upon review that turns out to be unnecessary. It's not supposed to be C btw, I has C# in mind when I wrote it (but of course logical right shifts are hardly exclusive to C#) – harold Nov 19 '14 at 12:59
  • http://pastebin.com/3xYQthgs here is the rewrite of your code into 1 function. It meets all the requirements and works! You can test here: http://writecodeonline.com/php/ (it uses 2 `do{}while` loops, which isn't bad). – Ismael Miguel Nov 19 '14 at 17:46
0

Depending on what endian-ness your machine is, you might have to reverse the order of bytes[0] and bytes1 below:

uint8_t  bytes[2] = { 0x04, 0x3f };
uint16_t result = (bytes[0] << 8) | bytes[1];

(This is in C, shouldn't be hard to translate to PHP etc., the languages and operators are similar enough)

Update: OK, now that you've clarified what you want, the basic approach is still the same. What you can do instead is count the number of bits in the right number, then do the bitshift as above on the left number, just with the dynamic number of bits. This works as long as you don't have more bits than fit into the largest numeric type that your language/platform support, so in this example 64 bits.

int  rightMaxBits = 0;
uint64_t leftNum = 0x04, rightNum = 0x3f;
uint64_t rightNumCopy = rightNum;

while( rightNumCopy )
{
    rightNumCopy >>= 1;
    rightMaxBits++;
}

uint64_t resultNum = (leftNum << rightMaxBits) | rightNum;

(Thanks for the bit-counting algo to this SO thread) For signed numbers, I'd suggest you use abs() on the numbers before you call this and then later re-apply the sign in whatever way you want.

Community
  • 1
  • 1
uliwitness
  • 8,532
  • 36
  • 58
  • Your solution assumes that those are bytes. What I am asking is 2 numbers (Quoting: `What I am asking is if it is possible to join all bits in 2 different` **`numbers`**). The difference being that it isn't restricted to only bytes. Your solution works great if the 2nd numbers is between 0 and 255. – Ismael Miguel Nov 19 '14 at 11:42
  • I have added 2 more examples of what I pretend, to clarify what I expect. – Ismael Miguel Nov 19 '14 at 11:49
  • It's the same if they're not single bytes. Just adjust the data types and the bitshift amount accordingly to how many bits your numbers have. – uliwitness Nov 19 '14 at 18:10
  • But numbers can be negative, positive, 32 bits, 64 bits, 0, -1 (i treat this one differently). Your solution requires knowing before hand what type you will use. Yes, that is a method to do it, but it only works for the case where `bytes[1]` is between 0 (`0x00`) and 255 (`0xff`). Another number like 256 (`0x1ff`, number by memory) will return bogus results. – Ismael Miguel Nov 19 '14 at 18:34
  • Well, your question didn't include any of those requirements, so I did not guess that's what you wanted. All I did was provide a solution to your original question. :-) – uliwitness Nov 20 '14 at 08:22
  • It would really help if instead of providing millions of examples, you wrote what you want to do, or what this is needed for. *How* do you want signs to be taken into account? Do you want -ff -fe to be shown as -ff-fe, do you want -fffe, do you want it to add them up and end at -1fd, I have no idea what you want nor what it would be good for. – uliwitness Nov 20 '14 at 08:26
  • What I want is that with 2 numbers (**any** integer number) you join all the bits (in the same order) to produce a new number. `-0xff` and `-0xfe` would become `-0xfffe`. It will be good for nothing, I just wanted to know if it was even possible to do this only using bitwise operations. I know you can do in any other ways (being one of them to treat each byte as a char and concatenating them together). – Ismael Miguel Nov 20 '14 at 10:33
  • @IsmaelMiguel OK, adapted my solution to the new requirements. – uliwitness Nov 21 '14 at 09:45
  • Your solution is now closer to what I asked, but you still have an increment (++) operator. The idea is to use **only and only bitwise operators**. (You can use boolean tricks, as long as it works for at least on php and javascript). I guess that replacing `rightMaxBits++;` with another operation like `a=abs(~a);` might solve the issue. – Ismael Miguel Nov 21 '14 at 11:48