고정밀도 연산 기법

일반적으로 프로그래밍에서 정수형 타입(int, long long 등)으로 표현할 수 없는 매우 큰 수를 다룰 때 고정밀도 연산이 필요합니다.

고정밀도 연산의 핵심은 큰 수를 배열이나 문자열 등을 사용하여 각 자릿수를 개별적으로 저장하고, 이를 바탕으로 덧셈, 뺄셈, 곱셈, 나눗셈 등의 산술 연산을 직접 시뮬레이션하는 것입니다. 이는 마치 사람이 연필과 종이를 사용하여 세로셈을 하는 것과 유사합니다.

고정밀도 덧셈

고정밀도 덧셈은 각 자릿수를 배열의 한 요소에 저장하는 방식으로 구현됩니다. 일반적으로 배열의 0번 인덱스를 일의 자리로, 1번 인덱스를 십의 자리로 사용하는 것이 편리합니다. 최상위 자리를 배열의 첫 요소로 사용할 경우, 올림(carry) 처리가 복잡해지고 배열 범위를 벗어나는 문제가 발생할 수 있습니다.

두 고정밀도 수의 덧셈

이 연산은 비교적 직관적입니다. 각 자릿수를 더하고 올림을 처리하는 과정을 반복합니다.

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

int main() {
    std::vector<int> result(1000, 0); // 결과를 저장할 배열 (최대 1000자리 가정)
    std::string num_str;
    int max_len = 0;

    for (int i = 0; i < 2; ++i) {
        std::cin >> num_str;
        int current_len = num_str.length();
        max_len = std::max(max_len, current_len);
        for (int j = 0; j < current_len; ++j) {
            // 문자열의 숫자를 역순으로 배열에 저장 (일의 자리부터)
            result[current_len - 1 - j] += (num_str[j] - '0');
        }
    }

    // 올림 처리
    for (int i = 0; i < max_len; ++i) {
        if (result[i] >= 10) {
            result[i + 1] += result[i] / 10;
            result[i] %= 10;
            if (i + 1 == max_len && result[i + 1] > 0) {
                max_len++; // 최고 자리에 올림이 발생하면 자릿수 증가
            }
        }
    }

    // 결과를 역순으로 출력
    while (max_len > 0 && result[max_len] == 0) {
        max_len--; // 맨 앞의 0 제거
    }
    for (int i = max_len; i >= 0; --i) {
        std::cout << result[i];
    }
    std::cout << std::endl;

    return 0;
}

고정밀도 수와 일반 정수형 덧셈

일반 정수형을 배열로 변환하여 고정밀도 덧셈과 동일한 방식으로 처리할 수 있습니다.

고정밀도 뺄셈

고정밀도 뺄셈은 덧셈과 달리 올림(carry) 대신 빌림(borrow)을 처리해야 합니다. 즉, 뺄셈 결과가 음수가 될 경우, 다음 자릿수에서 '1'을 빌려와야 합니다. 음수 결과가 나올 수 있으므로, 두 수의 크기를 먼저 비교하여 큰 수에서 작은 수를 빼고 필요한 경우 결과에 음수 부호를 붙입니다.

두 고정밀도 수의 뺄셈

#include <cstdio>
#include <cstring>
#include <algorithm> // std::swap 사용

const int MAXN = 10100;
int result[MAXN];
int num1_digits[MAXN], num2_digits[MAXN];
char s1[MAXN], s2[MAXN];
int len1, len2;

// 두 정수를 바꾸는 함수
void swap_ints(int& a, int& b) {
    a = a ^ b;
    b = b ^ a;
    a = a ^ b;
}

int main() {
    scanf("%s %s", s1, s2);
    len1 = strlen(s1);
    len2 = strlen(s2);

    // 문자열을 역순으로 배열에 저장 (일의 자리부터)
    for (int i = 0; i < len1; ++i) num1_digits[i] = s1[len1 - 1 - i] - '0';
    for (int i = 0; i < len2; ++i) num2_digits[i] = s2[len2 - 1 - i] - '0';

    int max_len = std::max(len1, len2);
    bool is_negative = false;

    // 두 수의 크기 비교
    for (int i = max_len - 1; i >= 0; --i) {
        if (num1_digits[i] > num2_digits[i]) break;
        if (num1_digits[i] < num2_digits[i]) {
            is_negative = true;
            break;
        }
        if (i == 0) { // 두 수가 같은 경우
            printf("0");
            return 0;
        }
    }

    if (is_negative) {
        printf("-");
        // num2가 더 크므로, num1과 num2를 swap하여 큰 수 - 작은 수 연산을 수행
        for (int i = 0; i < max_len; ++i) swap_ints(num1_digits[i], num2_digits[i]);
    }

    // 뺄셈 수행
    for (int i = 0; i < max_len; ++i) {
        result[i] = num1_digits[i] - num2_digits[i];
        if (result[i] < 0) {
            num1_digits[i + 1]--; // 다음 자릿수에서 빌림
            result[i] += 10;
        }
    }

    // 결과에서 맨 앞의 0 제거
    while (max_len > 0 && result[max_len] == 0) {
        max_len--;
    }

    // 결과 출력
    if (max_len < 0) printf("0"); // 결과가 0인 경우
    else {
        for (int i = max_len; i >= 0; --i) {
            printf("%d", result[i]);
        }
    }
    printf("\n");

    return 0;
}

고정밀도 수와 일반 정수형 뺄셈

덧셈과 마찬가지로, 일반 정수형을 배열로 변환하여 고정밀도 뺄셈 방식을 적용합니다.

고정밀도 곱셈

고정밀도 곱셈의 핵심은, 한 수의 i번째 자릿수와 다른 수의 j번째 자릿수를 곱한 결과가 최종 결과의 (i+j)번째 자릿수에 더해진다는 것입니다. 이는 세로셈 곱셈 원리를 그대로 따른 결과입니다.

두 고정밀도 수의 곱셈

#include <cstdio>
#include <cstring>
#include <vector>
#include <string>
#include <algorithm>

const int MAXN = 20010; // 곱셈 결과는 최대 두 수 길이의 합만큼 될 수 있음
int num1_digits[MAXN], num2_digits[MAXN];
int result[MAXN]; // 곱셈 결과를 저장할 배열
char temp_str[MAXN];

int main() {
    scanf("%s", temp_str);
    int len1 = strlen(temp_str);
    for (int i = 0; i < len1; ++i) num1_digits[i] = temp_str[len1 - 1 - i] - '0'; // 일의 자리부터 저장

    scanf("%s", temp_str);
    int len2 = strlen(temp_str);
    for (int i = 0; i < len2; ++i) num2_digits[i] = temp_str[len2 - 1 - i] - '0'; // 일의 자리부터 저장

    // 곱셈 수행
    for (int i = 0; i < len1; ++i) {
        for (int j = 0; j < len2; ++j) {
            result[i + j] += num1_digits[i] * num2_digits[j];
            // 올림 처리
            result[i + j + 1] += result[i + j] / 10;
            result[i + j] %= 10;
        }
    }

    int final_len = len1 + len2 - 1; // 예상되는 최대 길이
    // 결과에서 맨 앞의 0 제거
    while (final_len >= 0 && result[final_len] == 0) {
        final_len--;
    }

    if (final_len == -1) { // 결과가 0인 경우
        printf("0\n");
    } else {
        for (int i = final_len; i >= 0; --i) {
            printf("%d", result[i]);
        }
        printf("\n");
    }

    return 0;
}

고정밀도 수와 일반 정수형 곱셈

일반 정수형을 배열로 변환하여 고정밀도 곱셈과 동일한 로직으로 처리할 수 있습니다.

고정밀도 나눗셈

두 고정밀도 수의 나눗셈

이 연산은 구현이 복잡하여 일반적으로 "구현 보류" 상태입니다.

고정밀도 수와 일반 정수형 나눗셈

고정밀도 피제수를 앞에서부터 순차적으로 일반 정수형 변수로 변환하며, 일반 정수형 제수로 나눗셈을 수행합니다. 이 과정에서 몫과 나머지를 구하며, 몫은 결과로 출력하고 나머지는 다음 자릿수를 이어붙여 다시 피제수로 사용합니다.

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

int main() {
    std::string dividend_str; // 고정밀도 피제수
    long long divisor;        // 일반 정수형 제수
    std::cin >> dividend_str >> divisor;

    long long current_num = 0; // 현재 처리 중인 부분 피제수
    long long remainder = 0;   // 나머지
    bool leading_zero = true;  // 결과의 선행 0을 처리하기 위한 플래그

    // 나눗셈 수행
    for (char digit : dividend_str) {
        current_num = remainder * 10 + (digit - '0');
        long long quotient_digit = current_num / divisor;
        remainder = current_num % divisor;

        if (quotient_digit > 0 || !leading_zero) {
            std::cout << quotient_digit;
            leading_zero = false;
        }
    }

    // 피제수가 제수보다 작거나, 나눗셈 결과가 0인 경우
    if (leading_zero) {
        std::cout << "0";
    }
    std::cout << std::endl;

    return 0;
}

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

8월 6일 14:50에 게시됨