高精度原理介绍及代码实现

目录

高精度

引入

使用场景

实现原理

高精度加法

数据存储

加法实现

总代码

高精度减法

与加法的不同点:

总代码

高精度乘法

总代码

高精度除法

总结

总注意点

减法注意点

高精度

引入

所谓高精度并不是很高级难懂的东西,只是对传统的加减法模拟实现

使用场景

高精度算法(High Accuracy Algorithm)的出现是为了处理超大数据的数学计算问题。在一般的科学计算中,我们可能会遇到需要计算小数点后几百位甚至更多的数字,或者处理几千亿、几百亿这样的大数字。这些数字超出了标准数据类型(如整型、实型)能够表示的范围,因此无法直接在计算机中正常存储和计算

实现原理

在高精度算法中,我们并不会把数据用int,long long,double这种数据结构来存储,而是用数组来存储,接下来我就讲讲其原理

高精度加法

数据存储

先从简单的加减运算说起,我们拿 23 + 25这个例子来举例,根据小学所学知识,我们知道当我们计算时应该列出这样子的式子:

如果我们要进行加法,那么我们将从右加到左,一一进位

而我们的算法将这里的27处理为:A[2] = {7,2};25处理为:B[2] = {5,2};

他们的和仍然是一个数组:C[2] = {2, 5};

你可能好奇,为什么是倒着存?🧐🤨

俺在前面也说过:从右加到左,因此,当我倒着存时,最低位(27的最低位为7)就在A[0],而当A[0] + B[0]要进位时,进位的1就被加到了C[1],综上所述,倒着存是为了方便进位(i进的位直接存在了i + 1的位置)(也就是7 + 5进的位存在了C[1])

倒着存应该怎么写呢?🤨🤨🤨

很简单,我们要注意读入数据时不能把存数据的变量设置为int,long long了,而是要设置为string

//a + b = c
​
int A[505];//第一个加数的数组
int B[505];//第二个加数的数组
int C[506];//和的数组
int len_c;
int main()
{
    string a, b;//注意用字符串读入
    cin >> a >> b;
​
    int len_a = a.size(), len_b = b.size();//len_a表示a有几个数字(若a = 1,则len_a = 1,若a = 10,则len_a = 2)
    int len_c = max(len_a, len_b);//a 和 b的和c最初设置为a 和 b的最大值,因为要确保每位都参与加法,就得让位数多的每位都加上
    
    //逆序存
    for (int i = 0; i < len_a; i++)
        A[len_a - 1 - i] = a[i] - '0';//注意 - ‘0’,因为是字符读入,且逆序存
    for (int i = 0; i < len_b; i++)
        B[len_b - 1 - i] = b[i] - '0';//注意 - ‘0’
    Add();
    
    return 0;
}

接下来就是重要的加法实现了

加法实现

先看代码

void Add()
{
    for (int i = 0; i < len_c; i++)
    {
        C[i] += A[i] + B[i];
        C[i + 1] += C[i] / 10;
        C[i] %= 10;
    }
​
    if (C[len_c])
        len_c++;
}
  • for循环:for循环是从0开始的,这是因为我们存储的时候0位放的就是加数的最低位(27的7)

  • A[i] + B[i]:模仿传统加法(我们例子中就是:7 + 5)

  • C[i + 1] += C[i] / 10;:模仿进位(我们例子中就是:(7 + 5) / 10 = 1,因此在C[1]上进位了1)

  • C[i] %= 10; : 本位进位完后就要变为只有个位(我们例子中就是:(7 + 5) /%10 = 2,因此在C[0]上只有2)

  • if (C[len_c]) len_c++;:如果结果中最高位下一位不为0,说明len_c的长度要++

    如同:

    9 7 + 3,在len_c = max(len_a, len_b);后,len_c的长度为2,但是当计算完97 + 3 = 100后:之前的最高位(第二位)的下一位(第三位)不为0(100的1),长度变为3,因此要更新len_c

没想到吧,到这里就把高精度讲完啦,你看到这里很棒了哦,给你一个赞 d=====( ̄▽ ̄*)b

总代码

//a + b = c
​
int A[505];//第一个加数的数组
int B[505];//第二个加数的数组
int C[506];//和的数组
int len_c;
​
void Add()
{
    for (int i = 0; i < len_c; i++)
    {
        C[i] += A[i] + B[i];
        C[i + 1] += C[i] / 10;
        C[i] %= 10;
    }
​
    if (C[len_c])
        len_c++;
}
​
int main()
{
    string a, b;//注意用字符串读入
    cin >> a >> b;
​
    int len_a = a.size(), len_b = b.size();
    len_c = max(len_a, len_b);
    for (int i = 0; i < len_a; i++)
        A[len_a - 1 - i] = a[i] - '0';//注意 - ‘0’,且逆序存
    for (int i = 0; i < len_b; i++)
        B[len_b - 1 - i] = b[i] - '0';//注意 - ‘0’
​
    Add();
​
    for (int i = 0; i < len_c; i++)
        cout << C[len_c - 1 - i];//注意逆序输出,因为存的时候是逆序的
    return 0;
}

高精度减法

这也是类似的,高精度通用的就是:

  • 数据用字符串读入,用数组存数

  • 要倒着存储数据

  • 注意进位

  • 注意逆序输出

与加法的不同点:

敲重点了哦~ψ(`∇´)ψ

  • 要看两个数据谁更大,要把大的作为a,小的作为b

  • 若起初a < b,用标记标记该答案为负数,以便最后得出答案时输出符号

总代码

//与加法相似
//加了调转字符串的操作
int main()
{
    string str1, str2;
    cin >> str1;
    cin >> str2;
​
    //两个数字最大为10^10086,因此数组设为10088就可(咱图个吉利)
    int a[10088] = { 0 }, b[10088] = { 0 }, c[10088] = { 0 };
    
    int flag = 0;
    //标记是否进行了调转字符串
​
    //与加法不同的是减法要调转字符串,把位数多的放前面,方便计算
    //至于负号,可以先标记,最后再进行处理
​
    if (str2.size() > str1.size()/*如果起初的b > a,---->要调转a, b, 把原先的a 变为 b*/||(str2.size() == str1.size()&&str1<str2))
    {
        /*!!!!!!!!!!!!!!!!!!!!注意或者后面的情况,若只有或者前面部分的,若是3-4这种情况,则无法算出正确答案*/
        //str1<str2:前者的数字小于后面的数,虽然这是字符串,但仍然可以这样比较
        flag = 1;
        //标记进行了调换:方便最后输出 - 号
        swap(str1, str2);
        //调转的函数,具体的大家可以去自行了解
    }
​
    for (int i = 0; i < str1.size(); i++)
    {
        a[i] = str1[str1.size() - 1 - i] - '0';
        //减'0'别忘了
    }
​
    for (int i = 0; i < str2.size(); i++)
    {
        b[i] = str2[str2.size() - 1 - i] - '0';
    }
​
    //因为一开始就进行了调换最长的数放在str1,所以这里不需要取str1和str2中最长的赋值给len
    for (int i = 0; i < str1.size(); i++)
    {
        c[i] = a[i] - b[i];
        if (c[i] < 0)
        {
            a[i + 1]--;
            c[i] = a[i] + 10 - b[i];
            //记得给a[i]加10,就是模拟实际的计算
        }
    }
​
    //因为一开始就进行了调换最长的数放在str1,所以这里不需要取str1和str2中最长的赋值给len,叶不用进行len++
    //直接判断是否要进行len--
    int len = str1.size();
​
    /*是while,不是if*/
​
    while(c[len - 1] == 0 && len > 1)
        len--;
​
​
    if (flag == 1)//输出负号
        printf("-");
    for (int i = 0; i < len; i++)//仍然是倒着输出
    {
        printf("%d", c[len - 1 - i]);
    }
    printf("\n");
​
    return 0;
}

高精度乘法

总代码

int c[5000];
int len_a, len_b, len_c;
void mul(int a[], int b[])
{
    for (int i = 0; i < len_a; i++)
    {
        for (int j = 0; j < len_b; j++)
        {
            c[i + j] += a[i] * b[j];//注意是 +=
            c[i + j + 1] += c[i + j] / 10;
            c[i + j] = c[i + j] % 10;
        }
    }
​
    while (len_c > 1 && c[len_c - 1] == 0)
    //注意要len_c > 1,如果乘积为0,就会一直减下去,最后什么都不输出,这是错误的
    //注意是c[len_c - 1] == 0
        len_c--;
}
​
int main()
{
    string a, b;
    cin >> a >> b;
    len_a = a.size(), len_b = b.size();
    len_c = len_a + len_b;
    int aarr[2005], barr[2005];
​
    //倒着存
    for (int i = 0; i < len_a; i++)
        aarr[i] = a[len_a - 1 - i] - '0';//记得 - '0'
    for (int i = 0; i < len_b; i++)
        barr[i] = b[len_b - 1 - i] - '0';
    
    mul(aarr, barr);
​
    //cout << len_c << endl;
    //倒着输出
    for (int i = 0; i < len_c; i++)
        cout << c[len_c - 1 - i];
    return 0;
}

高精度除法

脑子不好的小菜鸟正在学习中::>_<::

总结

总注意点

综上所述,所谓的高精度就是模仿传统的运算法则,注意总点如下:

  • 不用数据类型存储数据,因为计算机存不下那么大的数字

  • 数组存储,且逆序存储

  • 注意进位,还有前置0

    (如:100 - 90 = 010,这个最高位的0是不输出的,要用如下句子处理掉:

    while (len_c > 1 && c[len_c - 1] == 0)
        //注意要len_c > 1,如果答案为0,就会一直减下去,最后什么都不输出,这是错误的
        //注意是c[len_c - 1] == 0
            len_c--;

减法注意点

  • 要看两个数据谁更大,要把大的作为a,小的作为b

  • 若起初a < b,要用标记标记该答案为负数,以便最后得出答案时输出符号

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/619100.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Kubernetes 核心概念

kubernetes的对象 Kubernetes 包含多种类型的资源对象&#xff1a;Pod、Label、Service、Replication Controller 等。 所有的资源对象都可以通过 Kubernetes 提供的 kubectl 工具进行增、删、改、查等操作&#xff0c;并将其保存在 etcd 中持久化存储。 Kubernets其实是一个…

【JAVA进阶篇教学】第十三篇:Java中volatile关键字讲解

博主打算从0-1讲解下java进阶篇教学&#xff0c;今天教学第十三篇&#xff1a;volatile关键字讲解。 在 Java 中&#xff0c;volatile关键字是一种轻量级的同步机制&#xff0c;用于确保变量的可见性和禁止指令重排序。本文将详细解释volatile关键字的工作原理、可见性保证以及…

对称加密介绍

一、什么是对称加密 对称密钥算法(Symmetric-key algorithm)&#xff0c;又称为对称加密、私钥加密、共享密钥加密&#xff0c;是密码学中的一类加密算法。 对称加密的特点是&#xff0c;在加密和解密时使用相同的密钥&#xff0c;或是使用两个可以简单地相互推算的密钥。 这…

Zynq开发-使用PYNQ快速入门摄像头MIPI驱动(OV5640)-overlay设计

目录 1. 简介 2. Overlay 设计 2.1 总体设计 2.2 MIPI 子系统 2.3 去马赛克 2.4 AXI_IIC 3. 注意事项 4. 总结 1. 简介 本文是关于《Zynq开发-使用PYNQ快速入门摄像头MIPI驱动(OV5640)-CSDN博客》的博客文章&#xff0c;涉及了overlay设计的Vivado工程细节。在使用PYN…

iZotope RX 11 for Mac 激活版:让您的音频焕发生机!

在追求音频完美的道路上&#xff0c;iZotope RX 11 for Mac是您的得力助手。它凭借先进的音频修复技术和丰富的音频增强工具&#xff0c;让您的音频作品焕发出前所未有的生机与活力。无论您是专业的音频工程师&#xff0c;还是业余的音乐爱好者&#xff0c;都能在这款工具中找到…

【Spring】验证 @ServerEndpoint 的类成员变量线程安全

文章目录 前言猜想来源验证方法Controller 的情况ServerEndpoint 的情况 后记 前言 最近有 websocket 的需求。探索 ServerEndpoint 的类成员变量特点。 这里类比 Controller 讨论 ServerEndpoint 类成员变量是否线程安全。 猜想来源 网上的教程大多数都这么展示程序&#…

OBS插件--音频采集

音频采集 音频采集是一款 源 插件,类似于OBS的win-capture/game-capture&#xff0c;允许从特定应用程序捕获音频&#xff0c;而不是捕获整个系统的音频。避免了因为特定音频的采集而需要引入第三方软件&#xff0c;而且时延也非常低。 下面截图演示下操作步骤&#xff1a; 首…

HCIP-Datacom-ARST自选题库_06_排障【28道题】

一、单选题 1.如果面对复杂的网络故障&#xff0c;并经过评估认为短时间内无法完成排障&#xff0c;而此时用户又急需恢复网络的可用性&#xff0c;那么正确的做法是? 告诉用户这是不可能实现的 不通知客户的情况下&#xff0c;直接搭建替代的网络环境 始终尝试排除故障&a…

前端AJAX与后台交互技术知识点及案例(续2)

以下笔记均为学习哔站黑马程序员AJAX视频所得&#xff01;&#xff01;&#xff01; AJAX作用&#xff1a;浏览器和服务器之间通信&#xff0c;动态数据交互 axios函数 先引入axios库&#xff0c;可在bootcdn中寻找相关js文件或者对应的script标签 axios({url:http://hmajax…

最大子矩阵:前缀和、动态规划

最近在学习动态规划&#xff0c;在牛客上刷题时碰到了这一题。其实最初的想法是暴力和前缀和&#xff0c;但是时间复杂度极高&#xff0c;需要套4层循环。后来去网上搜了一下相关的题解和做法&#xff0c;进而了解到了前缀和&#xff0b;线性动态规划的做法。但是在成功做出这题…

【爬虫基础1.1课】——requests模块上

目录索引 requests模块的作用&#xff1a;实例引入&#xff1a; 特殊情况&#xff1a;锦囊1&#xff1a;锦囊2: 这一个栏目&#xff0c;我会给出我从零开始学习爬虫的全过程。感兴趣的小伙伴可以关注一波&#xff0c;用于复习和新学都是不错的选择。 那么废话不多说&#xff0c…

接搭建仿美团、代付系统源码搭建教程

最近很多粉丝催更、分享一下地球号&#xff1a;xiaobao0214520(WX) 现在大家都很流行搞网恋&#xff0c;我们搭建一个跟美团相似的系统 然后开发一个好友代付&#xff0c;我们在点单的时候转发链接让网恋对象付钱 若只是单点外卖的话&#xff0c;能榨出的油水还是太少。 所以…

Redis的数据淘汰策略——Java全栈知识(19)

Redis的数据淘汰策略 什么是数据淘汰策略 数据过期策略是 redis 中设置了 TTL 的数据过期的时候 Redis 的处理策略。数据淘汰策略是 Redis 内存不够的时候&#xff0c; 数据的淘汰策略&#xff1a;当 Redis 中的内存不够用时&#xff0c;此时在向 Redis 中添加新的 key, 那么…

免费思维13招之八:跨行业思维

免费思维13招之八:跨行业思维 免费思维的另一大战略思维——跨行业型思维。 跨行业型思维有两种:一种是通过跨行业,把自己的产品免费掉,从而赚取其他行业的利润。另一种是通过跨行业,把别人的主流产品免费掉,从而增大自己产品的销量。 第一种,把自己的产品免费,从而赚…

ONES 功能上新 | 近期产品新功能一览

支持在 ONES Project 中通过弹窗查看、编辑 ONES Wiki 页面。 应用场景&#xff1a; 当需要在 ONES Project 中查看 ONES Wiki 的页面内容时&#xff0c;可以直接点击工作项关联的 ONES Wiki 页面或项目文档组件中的页面&#xff0c;即可在 ONES Project 中通过弹窗查看 ONES W…

问题解决记录 | 内存溢出

报错截图&#xff1a; 处理方式&#xff1a; 增大PDI工具的内存 打开Spoon.bat配置文件 修改配置

【Linux 网络】网络编程套接字 -- 详解

⚪ 预备知识 1、理解源 IP 地址和目的 IP 地址 举例理解&#xff1a;&#xff08;唐僧西天取经&#xff09; 在 IP 数据包头部中 有两个 IP 地址&#xff0c; 分别叫做源 IP 地址 和目的 IP 地址。 如果我们的台式机或者笔记本没有 IP 地址就无法上网&#xff0c;而因为…

与 Apollo 共创生态:Apollo 七周年大会带我体会自动驾驶技术的发展

前言 自动驾驶技术作为当今科技领域的热门话题&#xff0c;吸引着无数开发者和企业的目光。而在这个风起云涌的行业中&#xff0c;Apollo开放平台作为自动驾驶领域的领军者之一&#xff0c;扮演着不可或缺的角色。七年前&#xff0c;当Apollo开放平台刚刚起步时&#xff0c;也…

STM32串口通信入门

文章目录 一、串口协议和RS-232标准&#xff0c;以及RS232电平与TTL电平的区别1.串口通信协议2.RS-232标准3.RS232电平与TTL电平的区别4.USB/TTL转232“模块&#xff08;CH340芯片为例&#xff09; 二、补充实验&#xff08;一&#xff09;几个常见的库函数、结构体1.时钟配置函…

java入门-面向对象的三大特性

面向对象三大特性 封装 什么是封装 封装 是将代码及其处理的数据绑定在一起的一种编程机制&#xff0c;该机制保证了程序和数据都不受外部干扰且不被误用。 封装的作用 访问控制符 方法传参-值传递 传参类型是基本类型 程序案例&#xff1a; public static void main(St…