What About Subtraction?

What About Subtraction?

Nov 28, 2022·

3 min read

In the previous article, I wrote something about how to build a 8-bit adder by logic gates. Yes the adder is working but only for positive numbers. So what about subtraction? Let's see the solution in this article.

If you think about it, subtraction is actually another type of addition. Take 1 - 2 as an example, it is can be written as 1 + (-2). So in this way, the problem becomes how to handle negative number and how to add between positive numbers and negative numbers.

Remember that we only have 8 bits and nothing else. So to express negative numbers, we need at least 1 bit. So normally, we could just take the first bit as the sign bit. When the first bit is 0 then this is a positive number and when the first bit is 1, then its negative.

So with this method, we can express negative numbers. But it is not enough. We still have to figure out how to add between positives and negatives. This method isn't working for this task. So to solve the second problem, we use a method call Two's Complement to express negatives. I am not going to dig into details here, check the link if you want to know more.

The merit of this method is that we can add the numbers between positives and negatives just as it is. I don't even need to change the code of eightBitAdder from the previous article at all. What we need to do in code is just to write some utility functions to convert between numbers and bits with twos' complement method.

Now let's get started. First, we implement the twos' complement method and the reverse one.

function twosComplement(bits: Bit[]): Bit[] {
  // not every bits
  for (let i = 0; i < 8; i++) {
    bits[i] = not(bits[i]);
  }

  // plus 1
  for (let i = 7; i > 0; i--) {
    if (bits[i] === 0) {
      bits[i] = 1;
      break;
    } else {
      bits[i] = 0;
    }
  }

  return bits;
}

function reverseTwosComplement(bits: Bit[]): Bit[] {
  // minus 1
  for (let i = 7; i > 0; i--) {
    if (bits[i] === 1) {
      bits[i] = 0;
      break;
    } else {
      bits[i] = 1;
    }
  }
  // not all bits
  for (let i = 0; i < 8; i++) {
    bits[i] = not(bits[i]);
  }

  return bits;
}

Then we can write the function to convert numbers into bits.

function positive2Bits(n: number): Bit[] {
  return n
    .toString(2)
    .padStart(8, "0")
    .split("")
    .map((c) => (c === "0" ? 0 : 1));
}

/**
 * express number with two's complement
 */
function number2Bits(n: number): Bit[] {
  if (n >= 0) {
    return positive2Bits(n);
  } else {
    return twosComplement(positive2Bits(-1 * n));
  }
}

And the function to convert bits back to numbers.

function bits2Positive(bits: Bit[]): number {
  return parseInt(bits.join(""), 2);
}

/**
 * convert bits back to number with two's complement
 */
function bits2Number(bits: Bit[]): number {
  if (bits[0] === 0) {
    return bits2Positive(bits);
  } else {
    return -1 * bits2Positive(reverseTwosComplement(bits))
  }
}

Just as I said before, we don't need to change any code of the adder. With these utility functions, we can make a test again. And this time, we test with negative numbers.

let x = 23;
let y = 49;
console.log(bits2Number(eightBitAdder(number2Bits(x), number2Bits(y))), x + y);
// 72 72

y = -49
console.log(bits2Number(eightBitAdder(number2Bits(x), number2Bits(y))), x + y);
// -26 -26