CS 240 Lab 2

Data as Bits

CS 240 Lab 2

Data as Bits

In lab today, you will continue to learn about digital logic, and how bits are used to represent data. You will experiment with Boolean functions and basic gates, including some investigation of binary numbers. Some of the exercises will involve building physical circuits on your protoboard, as we learned to do last week. You will also use a simulator to simulate some circuits.

In the final exercises, you will begin to explore how to think about and solve bit puzzles.

Exercise 1: Binary Circuits

Hexadecimal representation is often used in place of or in conjunction with binary representation when working with binary numbers. This is because hexadecimal is more concise than binary, but is easy to convert to/from binary, since both bases (2 and 16) are powers of two. So, a single digit in hexadecimal is always a 4-digit value in binary, as follows:

Hex Binary
QD QC QB QA
0 0 0 0 0
1 0 0 0 1
2 0 0 1 0
3 0 0 1 1
4 0 1 0 0
5 0 1 0 1
6 0 1 1 0
7 0 1 1 1
8 1 0 0 0
9 1 0 0 1
A 1 0 1 0
B 1 0 1 1
C 1 1 0 0
D 1 1 0 1
E 1 1 1 0
F 1 1 1 1

Note the patterns in this truth table:

  • The least significant bit of the 4-bit value QA toggles every number.
  • The next-least-significant bit QB toggles every other number.
  • The QC toggles every four numbers.
  • The QD toggles every eight numbers.

An IC 7493 is plugged into your workbench at the top right. Examine the following pinout and logic diagram, and the accompanying picture of wires in the board:

A pinout diagram for the 7493 chip, showing the following labels for each pin (clockwise around the diagram from the top-left): Pin 1 is CKB, pin 2 is R0(1), Pin 3 is R0(2), Pin 4 is N.C., Pin 5 is Vcc, Pin 6 is N.C., Pin 7 is N.C. (that’s the left side). On the right, pin 8 is QC, pin 9 is QB, pin 10 is GND, pin 11 is QD, pin 12 is QA, pin 13 is N.C., and pin 14 is CKA. N.C. stands for “not connected.” 

A logic diagram of a 7493 chip with connections. It shows inputs A, B, R1, and R2 on the left of the chip, input Vcc and output GND on the top and bottom respectively, and outputs QA, QB, QC, and QD on the right side of the chip. Each input/output is labeled with a pin number and has connections indicated. Input A is pin 14 and connects to PB1 (a push-button input). Input B is pin 1 and connects around to input 12. Input R1 is pin 2 and connects to an external ground connection. R2 (pin 3) and the GND output (pin 10) also connect to ground. Vcc is pin 5 and connects to +5V. Output QA is pin 12 and connects both to pin 1/input A and to LED indicator #8. QB is pin 9 and connects to LED #7, QC is pin 8 and connects to LED #6, and QD is pin 11 and connects to LED #5. 

A 7493 chip connected to several inputs and outputs. The 7493 is plugged into a protoboard across the middle divide of the top-right section, so that each of its 14 pins are plugged into different rows of that section. Connections listed are made by plugging wires into open slots in the same row as the name pin. Ground and 5V rails are available on either edge of the protoboard, so those connections are short. Going around counterclockwise from the top-left pin, pin 1 is connected to pin 12, pin 2 is connected to ground, pin 3 is also connected to ground, pin 5 is connected to +5V, pin 8 is connected to logic indicator #6, pin 9 is connected to logic indicator #7, pin 10 is connected to ground, pin 11 is connected to logic indicator #5, pin 12 is connected to logic indicator #8 (and also back to pin 1, as mentioned already), and pin 14 is connected to a wire that goes out of the picture over to the 1st push-button input. Pins not mentioned are left disconnected. 

Note: “PB1” in the logic diagram refers to one of the “push-button” inputs on your board.

Which wire in the picture represents the Vcc connection?

Example answer: The short red wire connected to the third foot from the bottom on the left side of the chip.

Which wire in the picture represents the GND connection?

Example answer: The short black wire connected to the third foot from the bottom on the right side of the chip.

Which pin does the wire that leaves the edge of the picture connect to? Where is it going?

Example answer: It’s connected to the top-right pin. Since pins are counted counterclockwise around the chip from the top-left and there are 7 pins on each side, that’s pin #14. It’s going out-of-frame because it needs to connect to the push-button input all the way on the bottom-left side of the workbench.

Why are pins 8, 9, 11, and 12 connected to indicators numbered 6, 7, 5, and 8, instead of being connected to LEDs in the same order as the pins are listed?

Example answer: That’s because the logic diagram shows that outputs QA, QB, QC, and QD are connected on pints 12, 9, 8, and 11, i.e., NOT in increasing pin order. We want the LEDs to be in-order according to the output labels, not the pin numbers.

Now, go ahead and wire up the chip on your own board, as shown in the picture / according to the logic diagram. (Check the section on logic diagrams in the slides: you need to map the diagram to the actual physical layout of the chip.)

Turn the power on, and observe the four logic indicators. Press the PB1 button repeatedly, until all of them indicate low voltage.

Now continue pressing PB1, recording the logic indicator outputs (1 for on/high-voltage and 0 for off/low-voltage) in the table to the right until all of the indicators are off again (click on each cell to toggle between 0 and 1).

Do you recognize this pattern? Describe what the 7493 chip does concisely:

Example answer: It’s a 4-bit binary counter that counts up by 1 each time the button is pressed and then released.

Now turn off the power, but don’t disconnect the circuit.

GCorrect answer:

Presses QD QC QB QA
0 0 0 0 0
1 0
1
0
1
0
1
0
1
2 0
1
0
1
0
1
0
1
3 0
1
0
1
0
1
0
1
4 0
1
0
1
0
1
0
1
5 0
1
0
1
0
1
0
1
6 0
1
0
1
0
1
0
1
7 0
1
0
1
0
1
0
1
8 0
1
0
1
0
1
0
1
9 0
1
0
1
0
1
0
1
10 0
1
0
1
0
1
0
1
11 0
1
0
1
0
1
0
1
12 0
1
0
1
0
1
0
1
13 0
1
0
1
0
1
0
1
14 0
1
0
1
0
1
0
1
15 0
1
0
1
0
1
0
1

Correct answer: See above

Exercise 2: Hex Display

Below the 7493 chip on your board is a TIL311 chip. We’ll connect it to the outputs of the 7493 to see how it works.

Follow the logic diagram shown below to connect the TIL311, referring to the logic diagram of the 7493 that we’ve shown above as well, and to the pinout for the TIL311 also shown below. From the 7493 to the TIL311, You’ll need to connect QA to A0, QB to A1, QC to A2, and QD to A3. Then wire up the Vcc, GND, and enable inputs of the TIL311 so that it is activated. The picture on the right shows what this should look like when connected on your board:

A pinout for the TIL 311, showing pins 1-7 down the left-hand side and pins 8-14 up the right-hand side. The pins are labeled: 1: LED supply V-led. 2: Latch data input B. 3: Latch data input A. 4: Left D.P. Cathode. 5: Latch strobe input. 6: No pin. 7: Ground. 8 (at bottom right): Blanking input. 9: No pin. 10: Right D.P. Cathode. 11: No pin. 12: Latch Data Input D. 13: Latch Data input C. 14: Logic Supply Vcc. The middle of the chip has a bunch of small black squares lined up in the pattern of a square 8 (these symbolize the LEDs on the chip which can light up in various patterns to display different digits). 

A logic diagram of a TIL311 chip with connections. It shows inputs A0, A1, A2, and A3 on pins 3, 2, 13, and 12 respectively. These are each connected to something outside the diagram. The Vcc pin on top is pin 14, and connects to +5V; pin 1 also connects there. Pins 5, 8, and 7 connect to ground; pin 7 is labeled GND and pin 5 is labeled “Enable” with a bar above it. There are no outputs in the diagram. 

A picture of both the 7493 and the TIL311 connected on the protoboard. The 7493 sits about 10 rows above the TIL311. The 7493 is connected as shown in the picture above, but with 4 extra wires connecting pins on the 7493 to pins on the TIL311: pin 8 to pin 13, pin 9 to pin 2, pin 11 to pin 12, and pin 12 to pin 3. The other connections from the TIL311 are: pins 1 and 14 to +5V, pins 5, 7, and 8 to ground. Red LEDs built into the clear red face of the TIL311 display the digit 0, and circuit traces internal to the chip can also be seen below the clear face. 

Once you have completed your wiring, power the board back on and record the symbols displayed on the TIL311:

  1. What symbol is displayed when the outputs of the 7493 are all 0? Correct answer: 0
  2. What symbol is displayed when the outputs of the 7493 are 0101? Correct answer: 5
  3. What symbol is displayed when the outputs of the 7493 are 1010? Correct answer: A
  4. What symbol is displayed when the outputs of the 7493 are 1111? Correct answer: F

What do these symbols represent? What is the purpose of the TIL311, and how is it related to what the 7493 does?

Example answer: These are the hexidecimal digits corresponding to the binary inputs, interpreted as a single integer value. The purpose of the TIL311 is to display a 4-digit binary number as a hex digit that humans can easily read. Since the 7493 counts clock pulses as a 4-wire binary signal, the TIL311 works well with it to take in 4 binary signals and display the corresponding number.

Exercise 3: Timing Diagrams

Using a simulator, you can create a timing diagram that shows patterns of high and low voltage over time. Although a truth table is sufficient to describe the behavior of a combinational circuit, for sequential circuits, a timing diagram is needed, since the outputs depend not only on the instantaneous state of the inputs, but on the history of activations over time.

To learn more about Circuitverse, we’ll hook up the same circuit you just built in the simulator. Consult the diagram and instructions below to hook things up:

A CircuitVerse layout of a circuit that has two buttons on the left, 5 debug flags across the top, a chip in the middle, and a breakout joining 4 wires into one bus connected to a single-digit hex display on the right. The chip has A and B inputs on the left and an R input on the bottom, plus QA, QB, QC, and QD outputs on the right (it’s a 74_93 analogue with one ‘reset’ input’). The debug flags each have a name and display the number 0, with a connection point at the bottom. Their names are ‘CLK’ (above and to the left of the chip), ‘QD’, ‘QC’, ‘QB’, and ‘QA’ (in that order in a horizontal line above and to the right of the chip). The breakout is lined up with the QA, QB, QC, and QD outputs of the chip, placed to the right of the debug outputs, with wires numbered 0, 1, 2, and 3 from the top to bottom joining together into a single bus that points down. The hex display reads ‘0’. The following connections are made: 1. The top button lines up with and connects to the ‘A’ input of the chip. That wire also splits in the middle and the extra end goes upwards to connect to the ‘CLK’ debug flag. The bottom button is slightly below the chip, and a wire connects it to the ‘R’ input on the bottom of the chip. The ‘QA’ output of the chip is connected back around the top from the right side over to ‘B’ input on the left side of the chip. The QA, QB, QC, and QD outputs are each connected straight across to the 0, 1, 2, and 3 inputs of the breakout, plus those wires each have an upward branch that connects them to the respective debug flags (QA to QA, QB to QB, etc.). As already stated, the bus from the breakout connects to the input on the top of a hex display below it. 

  1. Download this “timing and parity” project file. Then go to https://circuitverse.org/simulator and wait for the simulator to load 1.
  2. From the “Project” menu choose “Import File.” Click on the ‘+’ to select a file and upload the timing_parity.cv file you just downloaded. You should now be in a ‘timing’ tab that has a button, 4 labeled boxes with ’X’s in them, and a “breakout” that has 4 numbered connections joined to a diagonal wire.
  3. Add the following elements to the circuit:
    1. A new ‘debug flag’ called ‘CLK’. Look under ‘Misc’ in the “Circuit Elements” panel for the yellow flag. Click on it, then move your mouse to position it next to the QA/QB/QC/QD flags on the left. While it’s selected, change the “Debug Flag Identifier” in the Properties panel to “CLK.”
    2. A second button (a gray circle). You can copy-paste the button that’s in there, or look under ‘Input’ in the “Circuit Elements” panel.
    3. The 74_93 chip you want to use. Since it’s been defined in another tab of this project, go to the ‘Circuit’ menu up top and choose ‘Insert SubCircuit.’ Position it below the debug flags, in between the ‘CLK’ flag and the row of ‘Q’ flags. Lining it up with the breakout with the 4 numbered wires will be useful.
    4. Add a hex display, found under ‘Output’ in the “Circuit Elements” panel. Put it underneath the breakout (the thing with the numbered wires).
  4. Now that you’ve got all the necessary components, hook them up as follows by dragging from the green dots to create wires:
    1. Hook the top button up to the ‘A’ input of the 74_93, and also connect the ‘CLK’ debug flag to that wire.
    2. Hook the second button up to the ‘R’ input of the 74_93 so you can reset it.
    3. Connect the QA, QB, QC, and QD outputs to the wires 0, 1, 2, and 3 of the breakout.
    4. Drag wires down from the QA through QD debug flags so they each connect to the wire from their respective output on the 74_93.
    5. Connect the bus from the breakout to the hex display.
    6. Connect the QA output of the 74_93 back around to the B input.
  5. Check that your connections match the screenshot shown above.

When you’ve built the circuit, open up the “Timing Diagram” panel by clicking the little arrow. Click on the clock button (the top button which is connected to the “CLK” debug flag), and you should be able to see the ‘CLK’ signal go up and then down again (you may have to unpause or reset the timing window). Hit the ‘reset’ button once to reset it (the bottom button), and then start hitting the ‘CLK’ button.

How many times you have to push ‘CLK’ to get the ‘QD’ signal to turn on after resetting it? Correct answer: 8

How many more times do you need to push ‘CLK’ to get ‘QD’ to turn off again after it turns on? Correct answer: 8

After pressing the button a bunch, your timing window should look something like the image below. Swap over to the ‘timing-solution’ tab, where we’ve replaced the clock button with an automatic clock. You should see the same pattern as in the screenshot below (you can hit the ‘pause’ button the timing window to pause the scrolling).

A timing window showing signals QA, QB, QC, QD, and CLK. The CLK signal goes up and then quickly down again repeatedly at a regular interval, spending the same amount up/high as down/low. The QA signal flips from high to low and back each time the CLK signal falls from high to low, showing a total of 8 high pulses over the course of 16 clock pulses, each as long as the distance between two of the CLK pulses’ ends. The QB signal works the same way, but triggers when QA falls from high to low, so it has 4 pulses that are each double the width of the QA pulses. Similarly, QC has 2 pulses triggered by the falling edges of QB, and QD has 1 very long pulse started and stopped by the falling edges of QC. 

How long is each high-voltage-period of the QD signal relative to the high-voltage periods of the clock signal (relative to one high-voltage period, NOT to a full high/low cycle)?

Example answer: 16 times as long.

Referring to the diagram above, imagine that the bottom signal in that diagram (the clock signal which has the shortest pulses) is generated by pressing a button, with each high pulse happening while the button is held down and each low pulse when the button is released. In that scenario, do the QA-QD signals (the four above the clock signal) update when the button is pressed, or when it is released?

When it’s pressed.
When it’s released.

Describe how the timing diagram is related to the truth table for the binary counter:

Example answer: At each time point in the timing diagram, we can see whether each signal is low or high. In the truth table for binary numbers, the QA column flips between 0 and 1 in each row, then the QB flips every other row, the QC every 4 rows and QD every 8 rows. In the timing diagrams, we have the same relationship between signals and clock ticks: the QA signal flips from high to low on the down-tick of each clock pulse, QB flips every other clock pulse, QC every four pulses, and QD every 8.

Exercise 4: Circuit Design

A parity bit is an extra bit of information which is sent when data is transmitted, to check for errors in transmission.

For a given set of bits, the occurrences of bits whose value is 1 is counted. If that count is odd, the parity bit value is set to 1, making the total count of occurrences of 1’s in the whole set (including the parity bit) an even number. If the count of 1’s in a given set of bits is already even, the parity bit value is 0, so in that case the count remains even for the whole set.

Fill in the truth table at the right for P, to produce an even parity bit for inputs ABCD.

An XOR or “exclusive or” gate is useful in a circuit to produce a parity bit. Why?

Example answer: When combined with XOR, high bits will cancel each other out, but will persist when paired with a low bit. So for the 2 bits going into an XOR gate, the result of the XOR will be a 1 if there were an odd number of input bits turned on, and a 0 if there were an even number turned on.

Go to the ‘parity’ tab of the CircuitVerse simulator. That tab has 4 inputs and 1 output already set up. Using 2-input XOR gates from the ‘Gates’ section of the “Circuit Elements” panel, design a circuit so that the ‘P’ output is a parity bit for the 4 input bits (it should match the truth table you just built).

Note: you can use more than 2 gates, just don’t use gates with more than 2 inputs.

Describe your design:

Example answer: The inputs are grouped into two pairs and fed into two XOR gates. The outputs from these enter a third XOR gate to produce the parity bit. A screenshot from CircuitVerse: Four binary inputs feed into two XOR gates, with the top two and bottom two inputs each feeding into one of the two gates. Then the outputs from those two XOR gates feed into a third XOR gate, the output of which goes to the ‘P’ output of the circuit. In this picture, the top input is 1 and the rest are 0, so the output is a 1.

Test your design using 4-5 rows of the truth table you just completed.

GCorrect answer:

A B C D P
0 0 0 0 0
1
0 0 0 1 0
1
0 0 1 0 0
1
0 0 1 1 0
1
0 1 0 0 0
1
0 1 0 1 0
1
0 1 1 0 0
1
0 1 1 1 0
1
1 0 0 0 0
1
1 0 0 1 0
1
1 0 1 0 0
1
1 0 1 1 0
1
1 1 0 0 0
1
1 1 0 1 0
1
1 1 1 0 0
1
1 1 1 1 0
1

Correct answer: See above

Exercise 5: Bit Puzzles

On the next assignment, you will write some short C programs which implement functions using a limited set and number of C bitwise, logic, and arithmetic operations.

The functions you will write will perform some logical operation. You are given a skeleton for each function, and you will replace the “return” expression with one or more lines of C code that implements the function. Your code must conform to the following style:

int Funct(arg1, arg2, ...) {
    /* brief description of how your implementation works */
    int var1 = Expr1;
    ...
    int varM = ExprM;

    varJ = ExprJ;
    ...
    varN = ExprN;
    return ExprR;
}

Each “Expr” is an expression using ONLY the following:

  1. Integer constants 0 through 255 (0xFF), inclusive. You are not allowed to use big constants such as 0xffffffff.
  2. Function arguments and local variables (no global variables).
  3. Unary integer operations ! ~
  4. Binary integer operations & ^ | + << >>

Some of the problems restrict the set of allowed operators even further.

Each “Expr” may consist of multiple operators. You are not restricted to one operator per line.

You are expressly forbidden to:

  1. Use any control constructs such as if, do, while, for, switch, etc.
  2. Define or use any macros.
  3. Define any additional functions in this file.
  4. Call any functions.
  5. Use any other operations, such as &&, ||, -, or ?:
  6. Use any form of casting.
  7. Use any data type other than int. This implies that you cannot use arrays, structs, or unions.

You may assume that your machine:

  1. Uses 2s complement, 32-bit representations of integers.
  2. Performs right shifts arithmetically.
  3. Has unpredictable behavior when shifting an integer by more than 32 bits.

Note: Integers are represented as 32-bit values in our machines. But, it is cumbersome to write that many digits on paper when you are verifying your results by hand. So, pretend you only have 4-bit representation (it will usually translate to a correct solution for 32-bit numbers, as well).

Here is an example of the type of functions you will write:

/*
 * pow2plus1
 * returns 2ⁿ + 1, where 0 <= n <= 31
 */
int pow2plus1(int n) {
       return (1 << n) + 1;
}

Explain why shifting left is an important part of the solution:

Example answer: We’re not allowed to use a function like pow from math.c, and since n is a variable, without being able to use a loop, we can’t fall back on repeated multiplication to compute 2^n. However, shifting left multiplies by 2, so 2^n can be computed by using a left shift, which is the only option available to us for this problem.

For each of the following values for n, show that 2ⁿ + 1 is equivalent to (1 << n) + 1:

If you prefer, you can do your work on paper.

n = 0

Example answer: 2^0 + 1 is 1 + 1 or 2.
1 << 0 is 1, and 1 + 1 is also 2.

n = 1

Example answer: 2^1 is 2, plus 1 is 3.
1 << 1 is 10₂ which is 2 in base 10, plus 1 is 3.

n = 3

Example answer: 2^3 is 8, plus 1 is 9. 1 << 3 is 1000₂, which is 8 in base 10, plus 1 is also 9.


Now, write a solution for the following function, using the specified rules:

/*
 *   bitOr(int x, int y)     produces    x | y    using only ~ and &
 *   Example: bitOr(6, 5) = 7
 *   Legal ops: ~ &
 *   Max ops: 8
 */
int bitOr(int x, int y) {
  return 2;     // 2 is just a placeholder; replace it
}

Example answer:

/*
 *   bitOr(int x, int y)     produces    x | y    using only ~ and &
 *   Example: bitOr(6, 5) = 7
 *   Legal ops: ~ &
 *   Max ops: 8
 */
int bitOr(int x, int y) {
    return ~(~x&~y);
}

Show that your code works by expressing the following values as 4-bit binary numbers and showing your work:

x = 1, y = 3

Example answer:

x = 0001₂ y = 0011₂

~x = 1110₂ ~y = 1100₂

~x & ~y = 1110₂ & 1100₂ = 1100₂

(x & ~y) = 0011₂ = 3

x|y = 0001₂|0011₂ = 0011₂ = 3

x = 0, y = 5

Example answer:

x = 0000₂ y = 0101₂

~x = 1111₂ ~y = 1010₂

~x & ~y = 1111₂ & 1010₂ = 1010₂

(x & ~y) = 0101₂ = 5

x|y = 0000₂|0101₂ = 0101₂ = 5

x = 3, y = 4

Example answer:

x = 0011₂ y = 0100₂

~x = 1100₂ ~y = 1011₂

~x & ~y = 1100₂ & 1011₂ = 1000₂

(x & ~y) = 0111₂ = 7

x|y = 0011₂|0100₂ = 0111₂ = 7


Given a positive integer, write a function to find if it is a power of two or not.

/*
 * isPower2 - returns 1 if x is a positive value which is power of 2, and
 *     returns 0 otherwise
 *   Examples: isPower2(5) = 0, isPower2(8) = 1, isPower(-1) = 0,
 *     isPower2(0) = 0
 *   Legal ops: ! ~ & ^ | + <<  >>
 *   Max ops: 20
 *   Rating: 4
 */
int isPower2(int x) {
  return 2;     // 2 is just a placeholder; replace it
}

If you were writing a program in Python or Java, how would you detect a power of 2?

Example answer:

First thing I can think of is to use modulus with 2 followed by division in a loop until we end up with either a 1 somewhere as a result of the modulus (so it’s not a power of 2) or the value ends up as 1:

def ip2(x):
    while x > 1:
        if x % 2 != 0:
            return False
        x //= 2
    return True

If you cannot apply this strategy in C using the rules given for this assignment, study some values to see if you recognize any patterns in the numbers which may help you.

Write down the 4-bit values for numbers 0 to 7:

GCorrect answer:

Decimal Binary
0 Correct answer: 0000
1 Correct answer: 0001
2 Correct answer: 0010
3 Correct answer: 0011
4 Correct answer: 0100
5 Correct answer: 0101
6 Correct answer: 0110
7 Correct answer: 0111

Correct answer: See above

Which of these are powers of 2?

Example answer: 0001, 0010, 0100

Do you notice anything significant about the pattern of bits for the numbers that are powers of 2 compared to the other values?

Example answer: They’re the only patterns which include exactly 1. All other patterns have either 0 1’s or more than 1 1.

Experiment with how you can use the allowed operators to detect the powers of 2. You can use:

! logical NOT

~ bitwise complement

& bitwise AND ^ bitwise XOR | bitwise OR + addition

You can also use constant values up to 255 (0xFF) in your expressions.

Because all numbers which are a power of two have only one bit position set, one less than a power of two will always have all the bits lower than that position set and all the bits above that 0.

Here are the powers of 2 with the next smaller number, x + -1, below them:

Expr
x: 0001 0010 0100
x-1: 0000 0001 0011

Experiment with applying each of the allowed bitwise operators to the pairs of numbers x and x-1.

What operator gives you the same result for each pair?

Example answer: & operator gives 0000 for each pair.

Now, for the non-powers of two x, list x + -1 and apply the operator you discovered:

Non-powers of 2

GCorrect answer:

Expr
x: 0011 0101 0110 0111
x-1: 0010 0100 0101 0110
Correct answer: 0010 Correct answer: 0100 Correct answer: 0100 Correct answer: 0110

Correct answer: See above

Do you get the same result by applying the operator to these pairs of values as you did for the powers of 2?

Example answer: Nope, each of these gives a non-zero result when & is applied.

If your answer is no, you have found a way to detect a power of 2!

Write an expression that returns 0 for non-powers of 2 (Note: x-1 can be accomplished with x + ~0):

Example answer: !(x & (x + ~0))

If x = 0, will you get the correct result with this expression?

Example answer: No, because x + ~0 is 0000 + 1111, which is 1111, and 0000 & 1111 is 0, so which when ! is applied becomes 0001, which is wrong, because 0 is NOT a power of 2.

The answer should be no: so, the value 0 must be treated as a special case.

If the number is 0, you should simply return 0.

If the number is stored in x, what expression will return 0 when the number is 0?

Example answer: Just x…

One of the bitwise operators can be used to connect the two expressions so that when x is 0, return 0, otherwise, return the expression which detects a power of two.

Write the full expression:

Example answer:

My first attempt (does work…)

!(x & (x + ~0)) + ~!x + 1

Less complex second term using ^ to combine them:

!(x & (x + ~0)) ^ !x

Will this expression also return 0 for all negative numbers?

Example answer: Nope

If not, add to your expression to also detect a negative value:

Example answer:

This version uses x >> 31 to get just the sign bit of x, and then uses ! to produce a 0 for negative numbers (positive sign bit) and a 1 for positive numbers. By combining that using &, we effectively force the result to be 0 for all negative numbers, but leave the result as-is based on the rest of the expression for positive numbers.

!(x & (x + ~0)) ^ !x & ! (x >> 31)


  1. In Firefox, there’s a bug that prevents CircuitVerse from loading correctly most of the time, but it has a workaround:

    1. Open up the Firefox dev tools under the three-bars-menu -> more tools -> web developer tools.
    2. Click on the two arrows button near the top of the dev tools where it says “Inspector” and/or “Console” to pick a different tool, and select “Performance.”
    3. Click on “Start recording.” At this point the loading bar at the top of the page should speed up and the simulator should load. You can then click “Cancel recording” to stop the profiler.
    ↩︎