fix: calculate subtraction by zero (#70)

Previously, subtracting zero (x - 0) would throw an error due to mismatched
binary string lengths after two's complement negation, while other operations
involving zero worked correctly.

This change ensures that:
- Zero is properly handled during two's complement negation
- Binary strings are consistently padded to full width
- Subtraction by zero (x - 0) now correctly returns x

Adjust tests to accommodate two's complement of zero.

The calculator now behaves consistently for all basic arithmetic identities
involving zero.

Signed-off-by: Macweese <50101641+macweese@users.noreply.github.com>
This commit is contained in:
Macweese
2026-01-19 18:49:53 +01:00
committed by GitHub
parent 0192c51933
commit 2f8645c482
3 changed files with 15 additions and 15 deletions

View File

@@ -74,11 +74,11 @@ describe('calc.addSpace', () => {
describe('calc.numberOfBitsDisplayed', () => {
it('calculates number of bits', () => {
expect(calc.numberOfBitsDisplayed(1)).toBe(1);
expect(calc.numberOfBitsDisplayed(BigInt(-1))).toBe(1);
expect(calc.numberOfBitsDisplayed(BigInt(-1))).toBe(32);
expect(calc.numberOfBitsDisplayed(2)).toBe(2);
expect(calc.numberOfBitsDisplayed(3)).toBe(2);
expect(calc.numberOfBitsDisplayed(68719476735)).toBe(36);
expect(calc.numberOfBitsDisplayed(INT32_MIN_VALUE-1)).toBe(32);
expect(calc.numberOfBitsDisplayed(INT32_MIN_VALUE-1)).toBe(64);
});
});
@@ -264,12 +264,12 @@ describe("calc misc", () => {
it('promoteTo64Bit', () => {
const n = asInteger(-1);
expect(calc.toBinaryString(calc.promoteTo64Bit(n))).toBe("1");
expect(calc.toBinaryString(calc.promoteTo64Bit(n))).toBe("11111111111111111111111111111111");
});
it('binaryRepresentation', () => {
expect(calc.toBinaryString(Integer.int(-2147483647))).toBe("0000000000000000000000000000001");
expect(calc.toBinaryString(Integer.int(-2147483647))).toBe("10000000000000000000000000000001");
expect(calc.toBinaryString(asInteger(2147483647))).toBe("1111111111111111111111111111111");
});
@@ -282,7 +282,7 @@ describe("calc misc", () => {
const byte = Integer.byte(-127);
const int = Integer.int(-127);
expect(calc.numberOfBitsDisplayed(int)).toBe(7);
expect(calc.numberOfBitsDisplayed(int)).toBe(32);
expect(calc.numberOfBitsDisplayed(int.abs())).toBe(7);
// If there is only sign bit left, might as well show it
@@ -350,6 +350,8 @@ describe("calc.engine.", () => {
it("sub", () => {
// 4-bit examples
expect(calc.engine.sub("0011", "0001")).toBe("0010"); // 3-1=2
expect(calc.engine.sub("0100", "0000")).toBe("0100"); // 4-0=4
expect(calc.engine.sub("0100", "0000")).toBe("0100"); // -4-0=-4 (wrap)
expect(calc.engine.sub("0000", "0001")).toBe("1111"); // 0-1 -> -1
expect(calc.engine.sub("1000", "0001")).toBe("0111"); // -8-1 -> 7 (wrap)
});
@@ -427,7 +429,7 @@ describe("calc.engine.", () => {
expect(calc.engine.applyTwosComplement("010")).toBe("110");
expect(calc.engine.applyTwosComplement("110")).toBe("010"); // reverse
expect(calc.engine.applyTwosComplement("110")).toBe("010");
expect(calc.engine.applyTwosComplement("0")).toBe("10");
expect(calc.engine.applyTwosComplement("0")).toBe("0");
expect(calc.engine.applyTwosComplement("10101100")).toBe("01010100");
expect(calc.engine.applyTwosComplement("01010100")).toBe("10101100"); // reverse
});

View File

@@ -51,11 +51,9 @@ const calc = {
if(bin.length > bitSize!)
throw new Error(`Binary represenation '${bin}' is bigger than the given bit size ${bitSize}`)
const r = num.value < 0
? this.engine.applyTwosComplement(bin)
return num.value < 0
? this.engine.applyTwosComplement(bin.padStart(bitSize, '0')) // Pad BEFORE twos complement
: bin;
return bin.length !== bitSize ? r.substring(r.length-bin.length) : r;
},
lshift (num: Integer, numBytes : JsNumber) : Integer {
@@ -304,7 +302,7 @@ const calc = {
// If there exists no '1' concat 1 at the
// starting of string
if (lastIndex === -1)
return "1" + bin;
return bin;
// Continue traversal backward after the position of
// first '1'

View File

@@ -12,8 +12,8 @@ describe("formatter", () => {
const minusOne = BigInt(-1);
const n32 = new Integer(minusOne, 32);
const n64 = new Integer(minusOne, 64);
expect(formatter.bin(n32)).toBe("1");
expect(formatter.bin(n64)).toBe("1");
expect(formatter.bin(n32)).toBe("11111111111111111111111111111111");
expect(formatter.bin(n64)).toBe("1111111111111111111111111111111111111111111111111111111111111111");
expect(formatter.fullBin(n32)).toBe("11111111111111111111111111111111");
expect(formatter.fullBin(n64)).toBe("1111111111111111111111111111111111111111111111111111111111111111");
});
@@ -27,10 +27,10 @@ describe("formatter", () => {
});
it('formats negative binary numbers', () => {
expect(formatter.numberToString(-1, 'bin')).toBe("1");
expect(formatter.numberToString(-1, 'bin')).toBe("11111111111111111111111111111111");
expect(formatter.numberToString(-1, 'bin', 32)).toBe("11111111111111111111111111111111");
expect(formatter.numberToString(-0, 'bin')).toBe("0");
expect(formatter.numberToString(-2147483647, 'bin')).toBe("0000000000000000000000000000001");
expect(formatter.numberToString(-2147483647, 'bin')).toBe("10000000000000000000000000000001");
});
it('pads left', () => {