From 03aaa9ceeb1c90e4e6cd5e2f44adf832c6729fc1 Mon Sep 17 00:00:00 2001 From: Yadukrishnan Date: Thu, 25 Apr 2024 09:28:10 +0530 Subject: [PATCH 1/3] Added code to calculate twos complement of a number --- .../twoscomplement/TwosComplement.java | 80 +++++++++++++++++++ .../TwosComplementUnitTest.java | 34 ++++++++ 2 files changed, 114 insertions(+) create mode 100644 core-java-modules/core-java-numbers-7/src/main/java/com/baeldung/twoscomplement/TwosComplement.java create mode 100644 core-java-modules/core-java-numbers-7/src/test/java/com/baeldung/twoscomplement/TwosComplementUnitTest.java diff --git a/core-java-modules/core-java-numbers-7/src/main/java/com/baeldung/twoscomplement/TwosComplement.java b/core-java-modules/core-java-numbers-7/src/main/java/com/baeldung/twoscomplement/TwosComplement.java new file mode 100644 index 0000000000..0bff5abc11 --- /dev/null +++ b/core-java-modules/core-java-numbers-7/src/main/java/com/baeldung/twoscomplement/TwosComplement.java @@ -0,0 +1,80 @@ +package com.baeldung.twoscomplement; + +import java.math.BigInteger; + +public class TwosComplement { + + public static String decimalToTwosComplementBinary(BigInteger num, int numBits) { + if (!canRepresentInNBits(num, numBits)) { + throw new IllegalArgumentException(numBits + " bits is not enough to represent the number " + num); + } + var isNegative = num.signum() == -1; + var absNum = num.abs(); + + // Convert the abs value of the number to its binary representation + String binary = absNum.toString(2); + + // Pad the binary representation with zeros to make it numBits long + while (binary.length() < numBits) { + binary = "0" + binary; + } + + // If the input number is negative, calculate two's complement + if (isNegative) { + binary = performTwosComplement(binary); + } + + return formatInNibbles(binary); + } + + private static String performTwosComplement(String binary) { + StringBuilder result = new StringBuilder(); + boolean carry = true; + // Perform one's complement + StringBuilder onesComplement = new StringBuilder(); + for (int i = binary.length() - 1; i >= 0; i--) { + char bit = binary.charAt(i); + onesComplement.insert(0, bit == '0' ? '1' : '0'); + } + // Addition by 1 + for (int i = onesComplement.length() - 1; i >= 0; i--) { + char bit = onesComplement.charAt(i); + if (bit == '1' && carry) { + result.insert(0, '0'); + } else if (bit == '0' && carry) { + result.insert(0, '1'); + carry = false; + } else { + result.insert(0, bit); + } + } + + if (carry) { + result.insert(0, '1'); + } + + return result.toString(); + } + + private static String formatInNibbles(String binary) { + StringBuilder formattedBin = new StringBuilder(); + for (int i = 1; i <= binary.length(); i++) { + if (i % 4 == 0 && i != binary.length()) { + formattedBin.append(binary.charAt(i - 1)).append(" "); + } else { + formattedBin.append(binary.charAt(i - 1)); + } + } + return formattedBin.toString(); + } + + private static boolean canRepresentInNBits(BigInteger number, int numBits) { + BigInteger minValue = BigInteger.ONE.shiftLeft(numBits - 1).negate(); // -2^(numBits-1) + BigInteger maxValue = BigInteger.ONE.shiftLeft(numBits - 1).subtract(BigInteger.ONE); // 2^(numBits-1) - 1 + return number.compareTo(minValue) >= 0 && number.compareTo(maxValue) <= 0; + } + +} + + + diff --git a/core-java-modules/core-java-numbers-7/src/test/java/com/baeldung/twoscomplement/TwosComplementUnitTest.java b/core-java-modules/core-java-numbers-7/src/test/java/com/baeldung/twoscomplement/TwosComplementUnitTest.java new file mode 100644 index 0000000000..248727f2ab --- /dev/null +++ b/core-java-modules/core-java-numbers-7/src/test/java/com/baeldung/twoscomplement/TwosComplementUnitTest.java @@ -0,0 +1,34 @@ +package com.baeldung.twoscomplement; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +import java.math.BigInteger; + +public class TwosComplementUnitTest { + + @ParameterizedTest(name = "Twos Complement of {0} with number of bits {1}") + @CsvSource({ + "0, 4, 0000", + "1, 4, 0001", + "-1, 4, 1111", + "7, 4, 0111", + "-7, 4, 1001", + "12345, 16, 0011 0000 0011 1001", + "-12345, 16, 1100 1111 1100 0111" + }) + public void givenNumberAndBits_getTwosComplement(String number, int noOfBits, String expected) { + String twosComplement = TwosComplement.decimalToTwosComplementBinary(new BigInteger(number), noOfBits); + Assertions.assertEquals(expected, twosComplement); + } + + @Test + public void givenNumberOutsideBitsRange_throwException() { + Exception ex = Assertions.assertThrows(IllegalArgumentException.class, () -> { + TwosComplement.decimalToTwosComplementBinary(BigInteger.valueOf(8), 3); + }); + Assertions.assertEquals("3 bits is not enough to represent the number 8", ex.getMessage()); + } +} From 6d61ab37611fe39c8f753d32c14ee54605983541 Mon Sep 17 00:00:00 2001 From: Yadukrishnan Date: Thu, 25 Apr 2024 09:44:27 +0530 Subject: [PATCH 2/3] Added another approach for the calculation --- .../twoscomplement/TwosComplement.java | 36 +++++++++++++++++++ .../TwosComplementUnitTest.java | 15 ++++++++ 2 files changed, 51 insertions(+) diff --git a/core-java-modules/core-java-numbers-7/src/main/java/com/baeldung/twoscomplement/TwosComplement.java b/core-java-modules/core-java-numbers-7/src/main/java/com/baeldung/twoscomplement/TwosComplement.java index 0bff5abc11..094114422f 100644 --- a/core-java-modules/core-java-numbers-7/src/main/java/com/baeldung/twoscomplement/TwosComplement.java +++ b/core-java-modules/core-java-numbers-7/src/main/java/com/baeldung/twoscomplement/TwosComplement.java @@ -1,6 +1,7 @@ package com.baeldung.twoscomplement; import java.math.BigInteger; +import java.util.stream.Collectors; public class TwosComplement { @@ -74,6 +75,41 @@ public class TwosComplement { return number.compareTo(minValue) >= 0 && number.compareTo(maxValue) <= 0; } + public static String decimalToTwosComplementBinaryUsingShortCut(BigInteger num, int numBits) { + if (!canRepresentInNBits(num, numBits)) { + throw new IllegalArgumentException(numBits + " bits is not enough to represent the number " + num); + } + var isNegative = num.signum() == -1; + var absNum = num.abs(); + + // Convert the abs value of the number to its binary representation + String binary = absNum.toString(2); + + // Pad the binary representation with zeros to make it numBits long + while (binary.length() < numBits) { + binary = "0" + binary; + } + + // If the input number is negative, calculate two's complement + if (isNegative) { + binary = performTwosComplementUsingShortCut(binary); + } + + return formatInNibbles(binary); + } + + private static String performTwosComplementUsingShortCut(String binary) { + int firstOneIndexFromRight = binary.lastIndexOf('1'); + if (firstOneIndexFromRight == -1) { + return binary; + } + String rightPart = binary.substring(firstOneIndexFromRight); + String leftPart = binary.substring(0, firstOneIndexFromRight); + String leftWithOnes = leftPart.chars().mapToObj(c -> c == '0' ? '1' : '0') + .map(String::valueOf).collect(Collectors.joining("")); + return leftWithOnes + rightPart; + } + } diff --git a/core-java-modules/core-java-numbers-7/src/test/java/com/baeldung/twoscomplement/TwosComplementUnitTest.java b/core-java-modules/core-java-numbers-7/src/test/java/com/baeldung/twoscomplement/TwosComplementUnitTest.java index 248727f2ab..d48a010ffe 100644 --- a/core-java-modules/core-java-numbers-7/src/test/java/com/baeldung/twoscomplement/TwosComplementUnitTest.java +++ b/core-java-modules/core-java-numbers-7/src/test/java/com/baeldung/twoscomplement/TwosComplementUnitTest.java @@ -24,6 +24,21 @@ public class TwosComplementUnitTest { Assertions.assertEquals(expected, twosComplement); } + @ParameterizedTest(name = "Twos Complement of {0} with number of bits {1}") + @CsvSource({ + "0, 4, 0000", + "1, 4, 0001", + "-1, 4, 1111", + "7, 4, 0111", + "-7, 4, 1001", + "12345, 16, 0011 0000 0011 1001", + "-12345, 16, 1100 1111 1100 0111" + }) + public void givenNumberAndBits_getTwosComplementUsingShortcut(String number, int noOfBits, String expected) { + String twosComplement = TwosComplement.decimalToTwosComplementBinary(new BigInteger(number), noOfBits); + Assertions.assertEquals(expected, twosComplement); + } + @Test public void givenNumberOutsideBitsRange_throwException() { Exception ex = Assertions.assertThrows(IllegalArgumentException.class, () -> { From fb121ee6320221e5867a14643d6195a6ff8807dc Mon Sep 17 00:00:00 2001 From: Yadukrishnan Date: Thu, 25 Apr 2024 15:12:24 +0530 Subject: [PATCH 3/3] Used the correct method in test --- .../com/baeldung/twoscomplement/TwosComplementUnitTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core-java-modules/core-java-numbers-7/src/test/java/com/baeldung/twoscomplement/TwosComplementUnitTest.java b/core-java-modules/core-java-numbers-7/src/test/java/com/baeldung/twoscomplement/TwosComplementUnitTest.java index d48a010ffe..41bef15081 100644 --- a/core-java-modules/core-java-numbers-7/src/test/java/com/baeldung/twoscomplement/TwosComplementUnitTest.java +++ b/core-java-modules/core-java-numbers-7/src/test/java/com/baeldung/twoscomplement/TwosComplementUnitTest.java @@ -35,7 +35,7 @@ public class TwosComplementUnitTest { "-12345, 16, 1100 1111 1100 0111" }) public void givenNumberAndBits_getTwosComplementUsingShortcut(String number, int noOfBits, String expected) { - String twosComplement = TwosComplement.decimalToTwosComplementBinary(new BigInteger(number), noOfBits); + String twosComplement = TwosComplement.decimalToTwosComplementBinaryUsingShortCut(new BigInteger(number), noOfBits); Assertions.assertEquals(expected, twosComplement); }