4
\$\begingroup\$

Here is an attempt to sum even extremely very large numbers. As the extremely bigger numbers are fed to the method as String type this became possible. This method can accept even negative numbers also. However for positive numbers, the + sign must not be provided too.

import java.util.Arrays;
import java.util.Scanner;

public class SumOfLongNumbersInString {

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String firstNumber = scanner.nextLine();
        String secondNumber = scanner.nextLine();
        scanner.close();
        System.out.println(findSumOfLongNumbersInString(firstNumber, secondNumber));
    }

    public static String findSumOfLongNumbersInString(String firstNumber, String secondNumber) {
        boolean firstNumberNegativeFlag = false, secondNumberNegativeFlag = false;
        boolean negateFlagAtEnd = false;
        // Checking for positive or negative numerics only in the string
        if (!firstNumber.matches("[-]?\\d++") || !secondNumber.matches("[-]?\\d++")) {
            throw new IllegalArgumentException();
        }
        // Stripping the - symbol from the further calculation
        if (firstNumber.charAt(0) == '-') {
            firstNumberNegativeFlag = true;
            firstNumber = firstNumber.substring(1);
        }
        if (secondNumber.charAt(0) == '-') {
            secondNumberNegativeFlag = true;
            secondNumber = secondNumber.substring(1);
        }
        StringBuilder result = new StringBuilder();
        // Reverse both the numbers for the iteration of calculation to happen from left to right
        firstNumber = new StringBuffer(firstNumber).reverse().toString();
        secondNumber = new StringBuffer(secondNumber).reverse().toString();
        char[] firstNumberArray = firstNumber.toCharArray();
        char[] secondNumberArray = secondNumber.toCharArray();
        // Making the char arrays created from the strings to be of the biggest size amoung the both and pad with necessary 0 if required.
        if (firstNumber.length() > secondNumber.length()) {
            secondNumberArray = Arrays.copyOf(secondNumberArray, firstNumberArray.length);
            Arrays.fill(secondNumberArray, secondNumber.length(), firstNumberArray.length, '0');

        } else {
            firstNumberArray = Arrays.copyOf(firstNumberArray, secondNumberArray.length);
            Arrays.fill(firstNumberArray, firstNumber.length(), secondNumberArray.length, '0');
        }
        int addCarry = 0;
        // If the one of the number is negative and the other is positive, what we would need to do is subtraction.
        // Hence, keep the biggest amoung both as the first number the smallest as second number.
        if (firstNumberNegativeFlag ^ secondNumberNegativeFlag) {
            for (int i = firstNumberArray.length - 1; i >= 0; i--) {
                if(firstNumberArray[i] == secondNumberArray[i]) {
                    continue;
                } else if (firstNumberArray[i] < secondNumberArray[i]) {
                    char[] tempNumberArray = firstNumberArray;
                    firstNumberArray = secondNumberArray;
                    secondNumberArray = tempNumberArray;
                    negateFlagAtEnd = true;
                } 
                break;
            }
        // If both the number are negative, we would need to do addition only with flagging as negative symbol at the end.
        } else if(firstNumberNegativeFlag && secondNumberNegativeFlag) {
            negateFlagAtEnd = true;
        }
        // Do with the digit level calculation
        for (int i = 0; i < firstNumberArray.length || i < secondNumberArray.length; i++) {
            // Do an addition if both the numbers are positive or negative.
            if (!(firstNumberNegativeFlag ^ secondNumberNegativeFlag)) {
                int numeralDigitSum = addCarry + getNumeral(firstNumberArray[i]) + getNumeral(secondNumberArray[i]);
                int sumDigit = numeralDigitSum % 10;
                addCarry = numeralDigitSum >= 10 ? 1 : 0;
                result.append(sumDigit);
            // Do a subtraction, if one number is positive and the other is negative.
            // Here, as we made already, the first number will always be the biggest and second number will be the smallest.
            } else {
                int numberDigitSubtract = getNumeral(firstNumberArray[i]) - getNumeral(secondNumberArray[i]);
                if (numberDigitSubtract < 0) {
                    numberDigitSubtract = (10 + getNumeral(firstNumberArray[i])) - getNumeral(secondNumberArray[i]);
                    firstNumberArray[i+1]--;
                }
                result.append(numberDigitSubtract);
            }
        }
        result.append(addCarry);
        // Reverse the final result as we did a reversal initially
        String finalSum = result.reverse().toString();
        // Remove any trailing zeros.
        do {
            finalSum = finalSum.substring(1);
            if(finalSum.length() == 1 && finalSum.charAt(0) == '0') {
                break;
            }
        }  while (finalSum.charAt(0) == '0');
        // Flag with negation if required.
        if (!finalSum.equals('0') && negateFlagAtEnd) {
            finalSum = "-" + finalSum;
        }
        return finalSum;
    }

    // Mapper for character constant to it's numeric equivalent
    private static int getNumeral(char c) {
        String stringNumeral = c + "";
        switch (stringNumeral) {
        case "0":
            return 0;
        case "1":
            return 1;
        case "2":
            return 2;
        case "3":
            return 3;
        case "4":
            return 4;
        case "5":
            return 5;
        case "6":
            return 6;
        case "7":
            return 7;
        case "8":
            return 8;
        case "9":
            return 9;
        default:
            throw new IllegalArgumentException(); // Should never be reachable, kept for code sanity.
        }
    }
}
\$\endgroup\$
3
\$\begingroup\$

First, there's the BigInteger class that facilitates this already, so if you're not , then you may like to consider using that class.

Other pointers:

  • Since you appear to be on Java 7 at least, please consider using try-with-resources on your Scanner instance.
  • You should stick to using StringBuilder instead of StringBuffer for your firstNumber/secondNumber declarations.
  • Your getNumeral() method can be simplified as such:

    private static int getNumeral(char c) {
        if (Character.isDigit(c)) {
            return Character.digit(c, 10);
        }
        throw new IllegalArgumentException();
    }
    
\$\endgroup\$
  • \$\begingroup\$ Thanks for your review comments. Infact then I was trying to recreate a similar implementation of BigInteger for learning purpose \$\endgroup\$ – Thomas Mathew Aug 11 '15 at 9:17
  • \$\begingroup\$ try-with-resources on Scanner... but what is there to be really closed? IMHO, System.in doesn't qualify. \$\endgroup\$ – maaartinus Aug 12 '15 at 0:43
  • \$\begingroup\$ @maaartinus I think of it as 'do it for one, do it for all'... rather than be selective at what to use it for, it's easier to remember to do it for all. YMMV. :) \$\endgroup\$ – h.j.k. Aug 12 '15 at 1:18
  • \$\begingroup\$ Actually, with that stated, I think the only exception I've made for myself (probably on this site too) was for a StringReader... forgot what was that used for exactly. \$\endgroup\$ – h.j.k. Aug 12 '15 at 2:45
3
\$\begingroup\$

Bug

The negative sign calculation is off. Here are some inputs that show the problem:

-500
400
100

-400
500
-100

The problem is that you set negateFlagAtEnd = true if the absolute value of the second number is larger than the first. But you need to also consider which one of the two numbers was negative in the first place. To fix this, you could change your code to:

    if (firstNumberNegativeFlag ^ secondNumberNegativeFlag) {
        negateFlagAtEnd = firstNumberNegativeFlag;
        for (int i = firstNumberArray.length - 1; i >= 0; i--) {
            if(firstNumberArray[i] == secondNumberArray[i]) {
                continue;
            } else if (firstNumberArray[i] < secondNumberArray[i]) {
                char[] tempNumberArray = firstNumberArray;
                firstNumberArray = secondNumberArray;
                secondNumberArray = tempNumberArray;
                negateFlagAtEnd = !negateFlagAtEnd;
            } 
            break;
        }
    }

Bug 2

The part where you strip leading zeros is wrong. Here are sample inputs that demonstrate the problem:

500
500
0

900
900
800

The problem is that you automatically strip the first digit (the carried digit) from the result here:

    // Remove any trailing zeros.
    do {
        finalSum = finalSum.substring(1);
        if(finalSum.length() == 1 && finalSum.charAt(0) == '0') {
            break;
        }
    }  while (finalSum.charAt(0) == '0');

You need to check that the carried digit is actually a zero before you strip it:

    // Remove any trailing zeros.
    while (finalSum.charAt(0) == '0') {
        finalSum = finalSum.substring(1);
        if (finalSum.length() == 1) {
            break;
        }
    }

Getting a digit from a char

There is a simpler way of getting a digit from a char:

private static int getNumeral(char c) {
    return c - '0';
}

You don't need to check for bad inputs because the calling function already checked to make sure the string only contained digits.

\$\endgroup\$
  • \$\begingroup\$ Thanks a lot for your comment. It was really valuable. Corrected the code as per to your suggestions. \$\endgroup\$ – Thomas Mathew Aug 11 '15 at 9:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.