2

I need help with a programming lab assignment that I cant seem to figure out.

The question is :

Allow the user to input 4 integer values all between 0 and 15 inclusive. Store these 4 values into a single 32 bit integer named "packit" Allow the user to choose which of the 4 integers they wish to recover from "packit". An input of "1" will recover the first value input, a "2" the second, a "3" the third, etc. Use only bit manipulations to store and recover the values.

This is what I have so far:

#include <iostream>

using namespace std;

int getInput(){
    int in;
    cout << "Input a number: ";
    cin >> in;
    return in;
}

int main(){
    int input1, input2, input3, input4;
    int output1, output2, output3, output4;
    unsigned int data = 32; // makes room for the 4 integers
    input1 = getInput();
    input2 = getInput();
    input3 = getInput();
    input4 = getInput();

    data = data << 2; //makes room for the input
    data = data | input1;
    output1 = data >> 2;
    cout << output1;


    return 0;
}

I stopped at output1 just to test it, and it doesn't work. However, this is what I have come to after trying for hours. I'm not sure what I am doing wrong. I'm following the notes I copied off the board in class. Any help would be appreciated.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
n00b programmer
  • 31
  • 1
  • 1
  • 3
  • `unsigned int data = 32; // makes room for the 4 integers` not sur what you are expecting here. all this does is assign the value '32' to unsigned int. you probably want to set it to zero to start. – Joe May 03 '17 at 20:45
  • also you are only shifting 2 bits `<< 2` and `>> 2` but a range of 0-15 uses more than 2 bits... – Joe May 03 '17 at 20:46
  • and lastly `output1 = data >> 2` will give you the result AFTER the shift, not the value of the bits that were shifted off. – Joe May 03 '17 at 20:47
  • 2
    The question asks you to name the 32-bit integer "packit". You didn't even do that. – Klitos Kyriacou May 03 '17 at 20:56

3 Answers3

8

You can accomplish this through bit-manipulation or with a union. The union tends to be the more readable approach.

union
{
  unsigned char[4] data;
  unsigned long packed;
} packit;

data[0] = 1;
data[1] = 1;
data[2] = 1;
data[3] = 1;

std::cout << "packit = " << packit.packed;
// packit = 16843009
// 00000001 00000001 00000001 00000001

Note: Type punning via a union is only valid in C.

To use bit operations you need to perform both a shift along with an and or or operation to retrieve and set the bits correctly.

unsigned long packit = 0;

// Set data0
packit |= data0;

// Set data1
packit |= (data1 << 8)

// Set data2
packit |= (data2 << 16)

// Set data3
packit |= (data3 << 24)

Reading is the opposite of setting. You need to clear any values except what you want to read and shift it into the correct position.

unsigned char data0 = (packit & 0x000000FF);
unsigned char data1 = (packit & 0x0000FF00) >> 8;
unsigned char data2 = (packit & 0x00FF0000) >> 16;
unsigned char data3 = (packit & 0xFF000000) >> 24;
lcs
  • 4,227
  • 17
  • 36
  • 3
    Type-punning through unions is not allowed in c++. See http://stackoverflow.com/a/25672839/969365 – simon May 03 '17 at 20:52
  • 1
    Sometimes I think they used a dart board to figure out what features from C are still valid. Added a note about this. – lcs May 03 '17 at 21:00
  • you can also accomplish this using bitfields and let the compiler handle the bit shifting for you: `struct { unsigned int data1: 8; unsigned int data2: 8; unsigned int data3: 8; unsigned int data4: 8; } packit;` – Remy Lebeau May 03 '17 at 21:22
  • @gurka if you are going to type pun then [memcpy is probably the way to go](http://stackoverflow.com/a/31080901/1708801) – Shafik Yaghmour May 04 '17 at 18:12
6

You are not manipulating the bits correctly.

Values 0-15 require 4 bits max, but you are only shifting bits by 2 places, so you have room for handling values 0-3 only.

Lets break down your code, using 15 (1111 in binary) as an example.

This code:

data = data << 2; //makes room for the input

Takes the existing data (32, which is 00100000 in binary), shifts it 2 places to the left, becomes 10000000.

Then this code:

data = data | input1;

Adds the input (which you are not restricting in any way, per the assignment) in the lower bits, becoming 10001111.

This code:

output1 = data >> 2;

Shifts the total bits by 2 places to the right, becoming 00100011, which as you can see loses the lower two bits of the previous input value that has been stored.

Then this code:

cout << output1;

Outputs the remaining bits as-is, which consists of bits from two different values merged together, producing 35 as output.

So, you need to bit-shift in both directions by either 4 bits (the minimum needed to handle values 0-15, taking up 16 bits total) or 8 bits (the maximum allowed to fit 4 values into 32 bits). And in the case of right-shifting, you need to mask out bits that you don't want in the output.

Try something more like this instead:

Using 4-bit storage (the minimum needed for handling values 0-15):

#include <iostream>
#include <limits> 

using namespace std;

unsigned int getInput()
{
    unsigned int in;
    do
    {
        cout << "Input a number between 0-15 inclusive: ";
        if (cin >> in)
        {
            if ((in >= 0) && (in <= 15))
                break;
        }

        cin.clear();
        cin.ignore(numeric_limits<streamsize_t>::max(), '\n');
    }
    while (true);

    return in;
}

int main()
{
    unsigned int input1, input2, input3, input4;
    unsigned int packit, output;
    int which;

    input1 = getInput();
    input2 = getInput();
    input3 = getInput();
    input4 = getInput();

    packit = (input4 << 12) | (input3 << 8) | (input2 << 4) | input1;

    do
    {
        cout << "Which number to retrieve (1-4, or 0 to exit): ";
        if (cin >> which)
        {
            if (which == 0)
                break;

            if ((which >= 1) && (which <= 4))
            {
                output = ((packit >> (4*(which-1))) & 0x0F);
                cout << output << endl;
                continue;
            }
        }

        cin.clear();
        cin.ignore(numeric_limits<streamsize_t>::max(), '\n');
    }
    while (true);

    return 0;
}

Using 8-bit storage (the maximum allowed, which would actually allow for handling values 0-255 max):

#include <iostream>
#include <limits> 

using namespace std;

unsigned int getInput()
{
    unsigned int in;
    do
    {
        cout << "Input a number between 0-15 inclusive: "; // or 255
        if (cin >> in)
        {
            if ((in >= 0) && (in <= 15)) // or 255
                break;
        }

        cin.clear();
        cin.ignore(numeric_limits<streamsize_t>::max(), '\n');
    }
    while (true);

    return in;
}

int main()
{
    unsigned int input1, input2, input3, input4;
    unsigned int packit, output;
    int which;

    input1 = getInput();
    input2 = getInput();
    input3 = getInput();
    input4 = getInput();

    packit = (input4 << 24) | (input3 << 16) | (input2 << 8) | input1;

    do
    {
        cout << "Which number to retrieve (1-4, or 0 to exit): ";
        if (cin >> which)
        {
            if (which == 0)
                break;

            if ((which >= 1) && (which <= 4))
            {
                output = ((packit >> (8*(which-1))) & 0xFF);
                cout << output << endl;
                continue;
            }
        }

        cin.clear();
        cin.ignore(numeric_limits<streamsize_t>::max(), '\n');
    }
    while (true);

    return 0;
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • `unsigned int data` is not guaranteed to be 32 bits. – Klitos Kyriacou May 03 '17 at 20:58
  • 1
    but it is guaranteed to be at least 16 bits, which is all you need for the problem (assuming 4 bit shifts vs 8) – Joe May 03 '17 at 21:10
  • @KlitosKyriacou: yes, but it is *usually* 32 bits on most modern systems. Using a fixed-sized type like `uint32_t` or `uint_least32_t` would guarantee 32 bits, but such types where not standardized until C++11. – Remy Lebeau May 03 '17 at 21:20
  • 1
    thank you so much I misunderstood the question and probably should have tried to understand what I was doing before just jumping in with a non full understanding – n00b programmer May 03 '17 at 21:22
2

You must think to yourself, How many bits does it take to represent the number 15?

15 = 1111

It takes exactly 4 bits.

Now you need to store 4 of those numbers...so you will need 16 bits. Which means you can even store all those numbers in an int, short or 2 chars. Let's use ints as you have been told to do.

Ok going back to your code,

#include <iostream>

using namespace std;

int getInput(){
    int in;
    cout << "Input a number: ";
    cin >> in;
    return in;
}

int main(){
    int input1, input2, input3, input4;
    int output1, output2, output3, output4;
    unsigned int data = 32; // makes room for the 4 integers
    input1 = getInput();
    input2 = getInput();
    input3 = getInput();
    input4 = getInput();

    data = data << 2; //makes room for the input
    data = data | input1;
    output1 = data >> 2;
    cout << output1;


    return 0;
}

First this, unsigned int data = 32; // makes room for the 4 integers.

This does not make room for 4 integers as you assume. The number 32 is 10000 in binary.

What you actually want to do to make room for 4 integers is that whenever you want to store any of the numbers, you shift the bits in the integer to the left by 4, and then store the number by using the bitwise or (|) not =

So this is more of what you need to start with:

unsigned int data = 0;

data = (data << 4)|input1;
data = (data << 4)|input2;
data = (data << 4)|input3;

Now to retrieve say, input1, we do this:

(data >> 8)&((1 << 4)-1)

Why?

This is because input1 is followed by 8 bits belonging to input2 and input3, so we eliminate those, then to actually get the value there, we do the bitwise and & of 15 (1111), which is simply done because the width of the number stored is not greater than 15, and because 15 is the smallest number closest to those numbers and consists of all 1's.

input2 == (data >> 2)&((1 << 4)-1)

input3 == (data)&((1 << 4)-1)

Hope that helps

smac89
  • 39,374
  • 15
  • 132
  • 179