고정밀도 연산 기법

일반적으로 정수 덧셈, 뺄셈, 곱셈, 나눗셈은 프로그래밍 언어에서 기본적으로 제공하는 연산자 (`+`, `-`, `*`, `/`)를 사용하여 처리합니다. 하지만 처리해야 할 숫자의 길이가 100자리, 1000자리에 달하는 등 매우 길어질 경우, `int`나 `long long`과 같은 기본 자료형의 표현 범위를 초과하게 됩니다. 이럴 때 사용하는 것이 바로 고정밀도(High Precision) 연산 기법입니다.

1. 고정밀도 덧셈: A + B

두 개의 큰 숫자를 더하는 것은 사람이 손으로 계산하는 방식과 유사합니다. 숫자의 가장 낮은 자릿수부터 시작하여 더하고, 올림(carry)이 발생하면 다음 자릿수로 넘겨 처리합니다. 이 과정을 모든 자릿수에 대해 반복합니다. 계산의 편의를 위해 두 숫자 문자열을 뒤집어서 처리한 후, 결과를 다시 뒤집어 출력하는 방식을 자주 사용합니다.

예제: AcWing 791. 고정밀도 덧셈


#include <iostream>
#include <string>
#include <vector>
#include <algorithm>

std::string addLargeNumbers(std::string num1, std::string num2) {
    std::string sum = "";
    int carry = 0;
    int i = num1.length() - 1, j = num2.length() - 1;

    while (i >= 0 || j >= 0 || carry) {
        int digit1 = (i >= 0) ? num1[i--] - '0' : 0;
        int digit2 = (j >= 0) ? num2[j--] - '0' : 0;

        int currentSum = digit1 + digit2 + carry;
        sum += std::to_string(currentSum % 10);
        carry = currentSum / 10;
    }

    std::reverse(sum.begin(), sum.end());
    return sum.empty() ? "0" : sum;
}

int main() {
    std::string a, b;
    std::cin >> a >> b;
    std::cout << addLargeNumbers(a, b) << std::endl;
    return 0;
}

2. 고정밀도 뺄셈: A - B

고정밀도 뺄셈 또한 덧셈과 유사한 원리로 처리합니다. 다만, 빼는 숫자가 더 큰 경우 결과가 음수가 될 수 있습니다. 이 경우, `A - B` 대신 `-(B - A)` 형태로 계산하여 항상 양수 뺄셈을 수행하도록 합니다. 계산 후 결과 문자열에서 발생할 수 있는 선행 0은 제거해야 합니다. 두 숫자의 크기를 비교하는 함수가 필요합니다.

예제: AcWing 792. 고정밀도 뺄셈


#include <iostream>
#include <string>
#include <vector>
#include <algorithm>

// num1이 num2보다 크거나 같으면 true 반환
bool isGreaterOrEqual(const std::string& num1, const std::string& num2) {
    if (num1.length() != num2.length()) {
        return num1.length() > num2.length();
    }
    for (int i = 0; i < num1.length(); ++i) {
        if (num1[i] != num2[i]) {
            return num1[i] > num2[i];
        }
    }
    return true; // 두 숫자가 같음
}

std::string subtractLargeNumbers(std::string num1, std::string num2) {
    std::string diff = "";
    bool isNegative = false;

    if (!isGreaterOrEqual(num1, num2)) {
        std::swap(num1, num2);
        isNegative = true;
    }

    int borrow = 0;
    int i = num1.length() - 1, j = num2.length() - 1;

    while (i >= 0) {
        int digit1 = num1[i--] - '0';
        int digit2 = (j >= 0) ? num2[j--] - '0' : 0;

        int currentDiff = digit1 - digit2 - borrow;
        if (currentDiff < 0) {
            currentDiff += 10;
            borrow = 1;
        } else {
            borrow = 0;
        }
        diff += std::to_string(currentDiff);
    }

    std::reverse(diff.begin(), diff.end());

    // 선행 0 제거
    size_t firstDigit = diff.find_first_not_of('0');
    if (std::string::npos == firstDigit) {
        return "0";
    }

    return (isNegative ? "-" : "") + diff.substr(firstDigit);
}

int main() {
    std::string a, b;
    std::cin >> a >> b;
    std::cout << subtractLargeNumbers(a, b) << std::endl;
    return 0;
}

3. 고정밀도 곱셈: A * b

고정밀도 숫자와 일반 크기의 정수를 곱하는 것은 비교적 간단합니다. 큰 숫자의 각 자릿수를 정수 `b`와 곱하고, 올림을 처리하며 결과를 만들어 나갑니다. 큰 숫자가 0인 경우, 결과에서 선행 0을 제거해야 합니다.

예제: AcWing 793. 고정밀도 곱셈


#include <iostream>
#include <string>
#include <vector>
#include <algorithm>

std::string multiplyLargeByInt(std::string num, int multiplier) {
    if (multiplier == 0) return "0";
    if (num == "0") return "0";

    std::string product = "";
    int carry = 0;
    int i = num.length() - 1;

    while (i >= 0 || carry) {
        int digit = (i >= 0) ? num[i--] - '0' : 0;
        long long currentProduct = (long long)digit * multiplier + carry;
        product += std::to_string(currentProduct % 10);
        carry = currentProduct / 10;
    }

    std::reverse(product.begin(), product.end());
    
    // 선행 0 제거 (이 경우는 multiplier가 0이 아니면 발생하지 않음)
    size_t firstDigit = product.find_first_not_of('0');
     if (std::string::npos == firstDigit) {
        return "0";
    }
    return product.substr(firstDigit);
}

int main() {
    std::string a;
    int b;
    std::cin >> a >> b;
    std::cout << multiplyLargeByInt(a, b) << std::endl;
    return 0;
}

4. 고정밀도 나눗셈: A / b

고정밀도 숫자와 일반 크기의 정수를 나누는 연산 역시 곱셈과 유사하게 처리할 수 있습니다. 큰 숫자의 앞부분부터 시작하여 현재까지의 부분과 다음 자릿수를 결합한 값을 나누려는 정수 `b`로 나누고, 몫을 결과에 추가하며 나머지를 다음 자릿수 연산에 사용합니다. 연산 초기에 발생하는 선행 0은 제거해야 합니다.

예제: AcWing 794. 고정밀도 나눗셈


#include <iostream>
#include <string>
#include <vector>
#include <algorithm>

std::string divideLargeByInt(std::string num, int divisor) {
    if (divisor == 0) {
        // Division by zero error handling
        return "Error: Division by zero";
    }
    if (num == "0") return "0";

    std::string quotient = "";
    int remainder = 0;

    for (char c : num) {
        int currentDigit = c - '0';
        int temp = remainder * 10 + currentDigit;
        quotient += std::to_string(temp / divisor);
        remainder = temp % divisor;
    }

    // 선행 0 제거
    size_t firstDigit = quotient.find_first_not_of('0');
    if (std::string::npos == firstDigit) {
        return "0"; // 결과가 0인 경우
    }

    return quotient.substr(firstDigit);
}

int main() {
    std::string a;
    int b;
    std::cin >> a >> b;
    std::cout << divideLargeByInt(a, b) << std::endl;
    return 0;
}

태그: 고정밀도 연산 알고리즘 C++ 수학

7월 14일 21:45에 게시됨