迹忆客 专注技术分享

当前位置:主页 > 学无止境 > 编程语言 > C++ >

C++ 中的大整数

作者:迹忆客 最近更新:2023/09/03 浏览次数:

在本文中,我们将讨论在 C++ 中处理大整数。 首先,我们将讨论 C++ 中的原始整数类型及其范围,然后讨论如何处理大于原始整数类型限制的整数。


C++ 中的整数数据类型

下表列出了 C++ 中的基本数据类型。 第一列具有数据类型,第二列具有以字节为单位的大小,最后一列具有每种数据类型中可以存储的最大整数范围。

数据类型 大小(以字节为单位) 范围
short int 2 -32,768 - +32,767
unsigned short int 2 0 - +65,535
int 4 -2,147,483,648 - +2,147,483,647
unsigned int 4 0 - 4,294,967,295
long int 4 -2,147,483,648 - +2,147,483,647
unsigned long int 4 0 - 4,294,967,295
long long int 8 -9,223,372,036,854,775,808 - 9,223,372,036,854,775,807
unsigned long long int 8 0 - 18,446,744,073,709,551,615

如果您对范围有一些困惑,您可以检查,将大于范围的内容存储在特定类型中,并通过打印相同的变量来查看结果; 在此,我们仅举一个例子。

#include <iostream>

using namespace std;

int main(){
    short int x = 35000;
    cout << x << '\n';
    return 0;
}

输出:

-30536

您可能会对结果感到惊讶; 但是,您可以使用二进制补码存储概念轻松跟踪值 35000 到 -30536。

无论如何,跳转到最后一个数据类型,大小为 8 字节的 unsigned long long int。

unsigned long long int 可以存储的最大数字是 18,446,744,073,709,551,615。 这个数字的位数是20。

因此,我们不能以原始数据类型存储超过 20 位的数字,而我们可能必须处理大于所谓大整数的整数。


C++ 中的大整数

在C++中,我们可以存储巨大的大整数。 例如,我们可以在 char 数组中处理 500 位甚至更多的非常大的整数,其中每个数字都存储为一个字符元素。

在完整实现 BigInteger 类之前,我们先从类的数据成员开始讨论更小的组成部分,以便更好地理解。

char *digit;
int length;

用于存储大整数位数的数据成员长度。 字符数组digit是存储Big Integer的数字。

接下来,查看构造函数。

BigInteger(const char integer[]){
    length = findLength(integer);
    digit = new char[length];
    for (int i=length-1,j=0;i>=0;i--)
        digit[j++] = integer[i];
}

此构造函数接收 char 数组中的 Big Integer(来自用户或文件的输入)。 findLength 函数计算大整数中的位数。

根据长度,声明一个动态数组来存储数字。

最后,我们以相反的顺序存储数字,因此低位数字应该有较低的索引。 这种存储模式的设计目的是使算术运算更加方便。

接下来,查看流插入运算符。

friend ostream& operator << (ostream &out, const BigInteger &integer){
    for (int i=integer.length-1;i>=0;i--)
        out << integer.digit[i];
    out << '\n';
    return out;
}

为了打印整数,我们从左到右从高位到低位打印数字,就像我们通常从左到右表示数字一样。 最后,查看具有重载的 plus + 运算符的 BigInteger 类的完整实现。

#include <iostream>

using namespace std;

class BigInteger{
        char *digit;
        int length;
        int findLength(const char integer[]){
            int length = 0;
            for (;integer[length]!=0;length++);
            return length;
        }
    public:
        BigInteger(const int SIZE){
            //This constructor is used to store results, and the length will be determined later
            length = 0;
            digit = new char[SIZE];
        }
        BigInteger(const char integer[]){
            length = findLength(integer);
            digit = new char[length];
            //store digits left to right, store higher order digits on higher index
            for (int i=length-1,j=0;i>=0;i--)
                digit[j++] = integer[i];
        }
        BigInteger operator + (const BigInteger &secondInteger){
            //Take i,j for loop, take the carry to store carry of the addition operation
            //At the start, set carry to 0; in the process of addition, we will get carry
            int i, j, result, carry = 0;     //result to store sum of addition of digits
            //Find the length of a longer integer
            int length = this -> length;
            if  (length < secondInteger.length)    length = secondInteger.length;
            //Take variable answer,  set its length greater than the length of longer integer
            //The result may have carry, which makes size greater than existing integers
            BigInteger answer (length + 1);
            for (i = 0 ; i < this->length && i < secondInteger.length ;i++){
                result = this->digit[i] + secondInteger.digit[i] + carry - '0' - '0';
                carry = result / 10;
                answer.digit[i] = result % 10 + '0';
            }
            //Take a pointer and point it to a long integer
            const BigInteger *currentInteger = this;
            if (currentInteger->length < secondInteger.length)
                currentInteger = &secondInteger;
            //Add remaining digits to answer
            for ( ;i<currentInteger->length;i++){
                result = currentInteger->digit[i] + carry - '0';
                carry = result / 10;
                answer.digit[i] = result % 10 + '0';
            }
            //set the length of an answer now
            answer.length=length+1;
            //Check if there is carry or not
            if (carry == 0)    length--;//reduce length because there is no carry
            else    answer.digit[i] = carry + '0';
            return answer;
        }
        friend ostream& operator << (ostream &out, const BigInteger &integer){
            for (int i=integer.length-1;i>=0;i--)
                out << integer.digit[i];
            out << '\n';
            return out;
        }
};
int main(){
    BigInteger integer1("67234321234321234321234321234321234321234321234");
    BigInteger integer2("4321234321234321234321234321234321234321234321");
    cout << integer1 << integer2;
    cout << integer1 + integer2;
    return 0;
}

让我们快速讨论一下加号运算符。 对于两个大整数操作数,我们有两种可能性。

两个操作数可以具有相同的大小或不同的大小。

我们将涵盖它们两者,并假设不同大小的可能性。 因此,我们采用长度大于较大操作数大小的结果大整数来存储进位(可能需要也可能不需要)。

我们正在为较小的整数运行一个循环。 之后,我们通过从每个元素中减去“0”字符来提取两个操作数中的字符,因为数字 3 的 ASCII 代码是 51,数字 0 的 ASCII 代码是 48。

因此,如果我们用 51 减去 48,我们将得到 3,这是加法运算所需要的。

接下来,注意加法表达式中使用了变量进位。 初始账面价值为0; 然而,如果两位数相加,结果可能会超过 9,从而产生进位。

在第一个循环中,我们处理两个操作数的数字并将进位存储在下一个加法运算中使用的变量进位中。

在第一个循环之后,我们必须决定两个操作数中哪一个是大的。 选择后,我们将剩余的较大整数的数字存储在结果中。

然而,我们在整个表达式中都采用进位。

原因是,如果剩下的数字有 9999...9,并且上一次运算有 1 个进位,那么结果的所有数字都会为 0,最后我们会在其中加 1 答案。

最后,主函数显示了两个具有四十多位数字的大整数的示例。 我们打印两个大整数,然后将它们相加并打印结果。

输出:

67234321234321234321234321234321234321234321234
4321234321234321234321234321234321234321234321
71555555555555555555555555555555555555555555555

在输出中,我们已成功打印了两个大整数。 此外,我们已经成功执行了加法。

我们取了两个整数,这样结果就很容易验证。 最后,我们还给出了一个减法运算符。

//Here, we are assuming that we are handling positive integers only
//Therefore, the first integer should be larger or equal to the second integer
BigInteger operator - (const BigInteger &secondInteger){
//First store result in a temporary array and later create answer object         accordingly
    int operand1, operand2;
    int i, j, result, carry = 0; //result to store sum of addition of digits
    //length of the first object will always be greater than equal to second
    char *diff = new char[this->length];
    //Copy elements of the first operand into a temporary array
    for (i = 0 ; i < this->length;i++)
        diff[i] = this->digit[i];
    //Now take a difference of diff array with the second integer
    for (i = 0 ; i < secondInteger.length;i++){
        operand1 = diff[i] - '0';
        operand2 = secondInteger.digit[i] - '0';
        if (operand1 < operand2){
            operand1 += 10;//In subtraction carry is always 10
            for(j = i+1; diff[j] == 0 ; j++);
            //at left larger than 0 digits always exist
            diff[j]--;//Take 1 carry from left non-zero digit
            for (j-- ; j > i ; j--)//shift carry to right
                diff[j] = 9;
            operand1 += 10;//Add 10 to current digit to create effect of carry
        }
        result = operand1 - operand2;
        diff[i] = result + '0';
    }
    //Find the length of array difference by finding the leading zero digits on the left side
    int length = this->length;
    for (;length>=0 && diff[length]!=0;length--);
    BigInteger answer(&diff[length]);
    return answer;
}

我们不讨论减法运算符; 然而,一个中心假设是第二个操作数总是大于或等于第一个操作数。

最后一个问题是空间复杂性。 目前,我们的实现需要 N 个字节来存储多个 N 个数字(因为每个数字都需要一个字符空间)。

但是,我们可以通过在一个字符中存储两位数字而不是在一个字符中存储一位数字来减少这一空间。 这样可以节省几乎一半的空间。

我们来看看这个想法的实现。

#include <iostream>
using namespace std;

class BigInteger {
    char* digit;
    int digitsCount, bytesCount;
    int findLength(const char integer[]) {
        int length = 0;
        for (; integer[length] != 0; length++);
        return length;
    }
public:
    BigInteger(const char integer[]) {
        digitsCount = findLength(integer);
        if (digitsCount % 2 == 0)    bytesCount = digitsCount / 2;
        else                    bytesCount = digitsCount / 2 + 1;
        digit = new char[bytesCount];
        int i = digitsCount - 1, j = 0;
        //Handle n - 1 digit in a loop
        for (; i > 0; i -= 2) {
            //After subtracting 48 or ASCII of 0, the result will be in the right 4 bits only
            digit[j] = (integer[i] - '0');//In first 4 bits store even integer
            digit[j++] += ((integer[i - 1] - '0') << 4);//In last 4 bits store odd integer
        }
        //Handle the last digit separately in case of an odd number of digits only
        if (digitsCount % 2 == 1)    digit[j] = (integer[i] - '0');
    }
    friend ostream& operator << (ostream& out, const BigInteger& integer) {
        int i = integer.bytesCount - 1;
        if (integer.digitsCount % 2 == 1) {
            out << (int)integer.digit[i];
            i--;
        }
        for (; i >= 0; i--)
            out << ((integer.digit[i] & 0xF0) >> 4) << (integer.digit[i] & 0x0F);
        out << '\n';
        return out;
    }
};


int main() {

    BigInteger integer1("67234321234321234321234321234321234321234321234");
    BigInteger integer2("4321234321234321234321234321234321234321234321");
    cout << integer1 << integer2;
    return 0;
}

输出:

67234321234321234321234321234321234321234321234
4321234321234321234321234321234321234321234321

希望您已经了解了在 C++ 中处理大整数的想法,现在您可以在此实现中实现加号运算符。

上一篇:在 C++ 中使用 128 位整数

下一篇:没有了

转载请发邮件至 1244347461@qq.com 进行申请,经作者同意之后,转载请以链接形式注明出处

本文地址:

相关文章

在 C++ 中使用 128 位整数

发布时间:2023/09/02 浏览次数:170 分类:C++

在本文中,我们将讨论 C++ 中的 128 位整数。 我们还将了解为什么需要它以及 C++ 中可能的替代方案。

C++ 井字棋游戏

发布时间:2023/09/02 浏览次数:107 分类:C++

本文将讨论使用 C++ 中的条件语句和循环创建 tic tac toe 游戏。C++ 井字棋游戏

C++ 中的默认构造函数和 default 关键字

发布时间:2023/09/02 浏览次数:127 分类:C++

本文讨论 C++ 中的默认构造函数以及新引入的关键字 default。首先,让我们了解一下C++中的默认构造函数。 默认构造函数是一种特殊的构造函数,它没有参数,用于为类的数据成员设置默认值。

C++ 中的空构造函数

发布时间:2023/09/02 浏览次数:165 分类:C++

C++ 中的空构造函数是一种不执行任何操作的特殊类型构造函数。 编译器知道没有代码可以执行,因此不会为构造函数生成任何可执行代码。

C++ 中的结构体构造函数

发布时间:2023/09/02 浏览次数:74 分类:C++

这篇文章将讨论 struct 的使用以及使用 C++ 添加构造函数。C++结构体简介 struct 代表结构,是组合了一些基本类型变量的用户定义数据类型。 这些变量混合起来形成一个新的单元。

单链表的 C++ 复制构造函数

发布时间:2023/08/31 浏览次数:59 分类:C++

本文将首先讨论链表数据结构的概念以及使用它的合适场景。 然后,我们将讨论使用 C++ 的单链表和单链表的复制构造函数的紧凑实现。

C++ 二叉搜索树析构函数

发布时间:2023/08/31 浏览次数:161 分类:C++

本文将讨论使用 C++ 中的 delete 关键字为二叉搜索树创建析构函数。C++ 二叉搜索树析构函数

C++ 中负数的模数

发布时间:2023/08/31 浏览次数:121 分类:C++

在本文中,我们将发现余数和模数之间的差异。 我们将了解 % 运算符的基础知识。稍后,我们将了解 % 运算符在 Python 和 C++ 中的行为方式。 最后,我们将通过讨论在 C++ 中实现模数功能的几种

C++ 中最快的排序算法

发布时间:2023/08/31 浏览次数:150 分类:C++

本文将解释哪种排序算法在什么条件下表现最好。 条件包括数据结构的类型、排序数据的大小、数据排列和数据元素的范围。

扫一扫阅读全部技术教程

社交账号
  • https://www.github.com/onmpw
  • qq:1244347461

最新推荐

教程更新

热门标签

扫码一下
查看教程更方便