The following text was written in a Jupyter notebook and then converted to an html file. As such, formatting on mobile may or may not work.
This program stores an unsigned integer as two separate unsigned shorts and then adds them back together.
If the word size is 4 bytes or 32 bits and a short is 16 bits, then the following code should work... probably.
The entire code in markdown:
#include <iostream>
using namespace std;
// isolate first half
// right shift then left shift
int FirstHalf(unsigned word) {
unsigned x = ((word >> 16) << 16); // word size is 32 bits so half of a word is 16
unsigned short a = (x >> 16);
return a;
}
// isolate second half
// left shift then logical right shift
int SecondHalf(unsigned word) {
unsigned short b = ((word << 16) >> 16);
return b;
}
// adding the two shorts together to get the original number
// will need to shift a 16 bits to the left again in order for it to
// add up correctly
int AddTheShorts(unsigned short a, unsigned short b) {
unsigned c = (a << 16) + b;
return c;
}
// We used unsigned types since signed values do not like shifting that much...
void PrintEverything(unsigned word) {
cout << "original number: " << dec << a << " hex: 0x" << hex << a << endl;
// cout << hex; // this way the stream will output the number as a hex and not as a decimal
cout << "first half: " << dec << FirstHalf(word) << " hex: 0x" << hex << FirstHalf(word) << endl;
cout << "second half: " << dec << SecondHalf(word) << " hex: 0x" << hex << SecondHalf(word) << endl;
cout << "together: 0x" << AddTheShorts(FirstHalf(word), SecondHalf(word)) << endl << endl;
}
int main() {
unsigned a = 0x00000076;
unsigned b = 0x87654321;
unsigned c = 0x000000C9;
unsigned d = 0xEDCBA987;
unsigned f = 123456789; // they can be written in either hex or decimal format
PrintEverything(a);
PrintEverything(b);
PrintEverything(c);
PrintEverything(d);
PrintEverything(f);
return 0;
}
#include <iostream>
using namespace std;
int FirstHalf(unsigned word) {
unsigned x = ((word >> 16) << 16);
unsigned short a = (x >> 16);
return a;
}
The function FirstHalf takes in an unsigned variable and then shifts it's bits to the right by 16 bits. Since an integer is 32 bits, when we shift right 16 bits and then shift left 16 bits, we're able to isolate the first 16 bits of the variable. These 16 bits are then assigned to the variable unsigned short a. However, before we assign these bits to the variable a, we need to shift it left 16 bits again because we are trying to store this in the type short. If unsigned word has the hex representation of 0x87654321, then the variable x will now have the hex value 0x87650000. In order to store that into a short, we need to shift it left 16 bits to get 0x00008765. After doing so we can then store the value into the unsigned short a.
The reason we're using unsigned data types right now is because when using signed data types, the c++ compiler likes to sometimes use arithmetic shifts. In those cases when we try to perform a right shift, we might end up with unexpected results since the compiler will perform sign extension using the highest bit value. This in turn might end up giving us a negative result since the compiler is likely reading the variable using two's complement and if the sign extension happened to use 1s instead of 0s, the value stored in a would be different than what we want.
int SecondHalf(unsigned word) {
unsigned short b = ((word << 16) >> 16);
return b;
}
In the function SecondHalf, we take in a value and shift it's bits left 16 and then right. By doing so we do the reverse of function FirstHalf, isolating the last half of the number. If x was 0x87654321 like in the previous example, shifting right 18 bits should get us 0x43210000 and then shifing left 16 bits would get us 0x00004321. Storing this value in an unsigned short variable will keep the same bit pattern since the compiler will only be cutting off the extra 0s.
int AddTheShorts(unsigned short a, unsigned short b) {
unsigned c = (a << 16) + b;
return c;
}
The function AddTheShorts takes in two unsigned shorts and adds them together. This is the program that adds our two shorts together, getting the original. If we want to get the original value, we'll need to shift a, or the firsthalf left 16 bits so the two bit patterns don't overlap when we try adding them together.
void PrintEverything(unsigned a) {
cout << "original number: " << dec << a << " hex: 0x" << hex << a << endl;
cout << "first half: " << dec << FirstHalf(a) << " hex: 0x" << hex << FirstHalf(a) << endl;
cout << "second half: " << dec << SecondHalf(a) << " hex: 0x" << hex << SecondHalf(a) << endl;
cout << "together: 0x" << AddTheShorts(FirstHalf(a), SecondHalf(a)) << endl << endl;
}
PrintEverything as a function, does what you would expect it to. It prints out the decimal version of the number and then the hex. We have to include the 0x in a string because while c++ will read the hex value, it does not add in anything extra to demark that it is a hex. To even get the stream to change from reading values as decimals, we need to change the stream output using "hex". To change it back we would use "dec".
unsigned a = 0x00000076;
unsigned b = 0x87654321;
unsigned c = 0x000000C9;
unsigned d = 0xEDCBA987;
unsigned f = 123456789;
PrintEverything(a);
PrintEverything(b);
PrintEverything(c);
PrintEverything(d);
PrintEverything(f);
// the output
original number: 118 hex: 0x76 first half: 0 hex: 0x0 second half: 118 hex: 0x76 together: 0x76 original number: 2271560481 hex: 0x87654321 first half: 34661 hex: 0x8765 second half: 17185 hex: 0x4321 together: 0x87654321 original number: 201 hex: 0xc9 first half: 0 hex: 0x0 second half: 201 hex: 0xc9 together: 0xc9 original number: 3989547399 hex: 0xedcba987 first half: 60875 hex: 0xedcb second half: 43399 hex: 0xa987 together: 0xedcba987 original number: 123456789 hex: 0x75bcd15 first half: 1883 hex: 0x75b second half: 52501 hex: 0xcd15 together: 0x75bcd15
The code that inspired this:
Practice Problem 2.23 from 2.2.6 Expanding the Bit Representation of a Number
// logical right shift
int fun1(unsigned word) {
return (int) ((word << 24) >> 24);
}
// arithmetic right shift
int fun2(unsigned word) {
return ((int) word << 24) >> 24;
}
The above code performs a left shift and then either a logical or arithmetic right shift. (isolates the last two 8 bits of the parameter).
Some context:
Busesare typically designed to transfer fixed-sized chunks of bytes known as words. Thenumber of bytes in a word (theword size) is a fundamental system parameter thatvaries across systems. (8)
The book assumes that a word size is 4 bytes and that buses transfer only one word at a time. As such, in the above code a value of 32 bits is expected to be used as a parameter.