大数运算之C语言实现

news2025/1/30 13:09:10

一、 前言

在我们代码编程过程中,我们经常需要处理各种规模的数值。从日常工作中的一些简单算术在到科学研究中的复杂计算,数字无处不在。然而,当数值变的异常庞大时,就需要用到大数运算来进行实现。本文我们将介绍大数运算的基本概念以及C语言实现大数运算的方法。

二、 概念

我们常见的数据其数据范围大都在int(-2^31 ~ 2^31-1)或者long long( -2^62 — 2^62-1)之内,但在一些情况下,我们会遇到超过这个范围的数据,这些数据我们就称之为大数。当然,大数不只是大整数,对于位数较多的小数,如123456799.987654321也适用于大数运算。

大数运算涉及对超出常规数据类型范围的数字进行加减乘除,阶乘,取余等运算。对于大数,我们通常以字符数组的形式存储,因为单个变量无法容纳如此多的位数,其中每个数组元素都对应一个数位。而实现这些运算的大致思路都是模拟竖式计算的过程,即通过模拟进位,借位,取余操作完成计算。

三、 代码实现

下面我们将对大数进行加减乘除,阶乘,取余等运算。

  • 大数加法运算

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    // 函数声明
    char* reverseString(const char* str);
    char* addLargeNumbers(const char* num1, const char* num2);
    
    // 主函数
    int main() {
    	// 输入两个大数
    	char num1[1001]; // 假设大数的最大位数为1000,多加一位用于存储字符串结束符'\0'
        char num2[1001];
        printf("请输入第一个大数: ");
    	scanf("%1000s", num1); // 限制输入长度,防止缓冲区溢出
    	printf("请输入第二个大数: ");
    	scanf("%1000s", num2);
    
    	// 调用大数相加函数
    	char* result = addLargeNumbers(num1, num2);
    
    	// 输出结果
    	printf("结果: %s\n", result);
    
    	// 释放动态分配的内存
    	free(result);
    
    	return 0;
    }
    
    // 字符串反转函数
    char* reverseString(const char* str) {
    	int len = strlen(str);
    	char* reversed = (char*)malloc((len + 1) * sizeof(char)); // 分配内存用于存储反转后的字符串
    	for (int i = 0; i < len; i++) {
        	reversed[i] = str[len - 1 - i]; // 反转字符串
    	}
    	reversed[len] = '\0'; // 添加字符串结束符
    	return reversed;
    }
    
    // 大数相加函数
    char* addLargeNumbers(const char* num1, const char* num2) {
    	char* reversedNum1 = reverseString(num1); // 反转字符串,便于从低位开始相加
    	char* reversedNum2 = reverseString(num2);
    
    	int len1 = strlen(reversedNum1);
    	int len2 = strlen(reversedNum2);
    	int maxLen = len1 > len2 ? len1 : len2;
    	int carry = 0; // 进位
    	char* result = (char*)malloc((maxLen + 2) * sizeof(char)); // 分配内存用于存储结果,多加一位用于可能的进位和一位用于字符串结束符
    
    	// 从低位开始逐位相加
    	for (int i = 0; i < maxLen; i++) {
        	int digit1 = i < len1 ? reversedNum1[i] - '0' : 0;
        	int digit2 = i < len2 ? reversedNum2[i] - '0' : 0;
        	int sum = digit1 + digit2 + carry;
        	carry = sum / 10;
        	result[i] = sum % 10 + '0';
    	}
    
    	// 处理剩余的进位
    	if (carry) {
        	result[maxLen] = carry + '0';
        	maxLen++;
    	}
    
    	// 添加字符串结束符
    	result[maxLen] = '\0';
    
    	// 反转结果字符串,得到正确的顺序
    	char* finalResult = reverseString(result);
    	free(reversedNum1); // 释放反转后的输入字符串内存
    	free(reversedNum2);
    	free(result); // 释放中间结果字符串内存
    
    	return finalResult; // 返回最终结果字符串
    }
    

    演示:在这里插入图片描述

  • 大数减法运算

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <stdbool.h>
    
    // 函数声明
    char* reverseString(const char* str);
    char* subtractLargeNumbers(const char* num1, const char* num2);
     
    // 主函数
    int main() {
        // 输入两个大数
        char num1[1001]; // 假设大数的最大位数为1000,多加一位用于存储字符串结束符'\0'
        char num2[1001];
        printf("请输入被减数: ");
        scanf("%1000s", num1); // 限制输入长度,防止缓冲区溢出
        printf("请输入减数: ");
        scanf("%1000s", num2);
     
        // 验证被减数是否大于等于减数(简单比较字符串长度,如果不等则按字典序比较)
        if (strlen(num1) < strlen(num2) || (strlen(num1) == strlen(num2) && strcmp(num1, num2) < 0)) {
            printf("错误: 被减数必须大于等于减数。\n");
            return 1;
        }
     
        // 调用大数相减函数
        char* result = subtractLargeNumbers(num1, num2);
     
        // 输出结果
        printf("结果: %s\n", result);
     
        // 释放动态分配的内存
        free(result);
     
        return 0;
    }
     
    // 字符串反转函数(与大数加法中相同)
    char* reverseString(const char* str) {
        int len = strlen(str);
        char* reversed = (char*)malloc((len + 1) * sizeof(char));
        for (int i = 0; i < len; i++) {
            reversed[i] = str[len - 1 - i];
        }
        reversed[len] = '\0';
        return reversed;
    }
     
    // 大数相减函数
    char* subtractLargeNumbers(const char* num1, const char* num2) {
        char* reversedNum1 = reverseString(num1);
        char* reversedNum2 = reverseString(num2);
     
        int len1 = strlen(reversedNum1);
        int len2 = strlen(reversedNum2);
        int maxLen = len1;
        bool isNegative = false; // 标记结果是否为负数(本实现假设被减数大于等于减数,所以结果不为负数)
        char* result = (char*)malloc((maxLen + 1) * sizeof(char)); // 分配内存,注意这里不需要额外位用于借位,因为我们已经假设被减数足够大
     
        // 从低位开始逐位相减
        for (int i = 0; i < maxLen; i++) {
            int digit1 = i < len1 ? reversedNum1[i] - '0' : 0;
            int digit2 = i < len2 ? reversedNum2[i] - '0' : 0;
            int diff = digit1 - digit2;
     
            // 如果当前位不够减,则从前一位借位
            if (diff < 0) {
                diff += 10;
                // 注意:这里我们不需要修改前一位的值,因为我们是逐位计算并存储结果的
                // 但是在实际实现中,如果需要保留借位的过程,可以引入一个额外的数组来记录借位情况
            }
     
            result[i] = diff + '0';
        }
     
        // 去除结果前导零
        int start = 0;
        while (start < maxLen && result[start] == '0') {
            start++;
        }
     
        // 如果整个结果都是0,则结果应为"0"
        if (start == maxLen) {
            strcpy(result, "0");
        } else {
            // 将结果字符串前移,以去除前导零
            for (int i = 0; i < maxLen - start; i++) {
                result[i] = result[start + i];
            }
            result[maxLen - start] = '\0';
        }
     
        // 反转结果字符串,得到正确的顺序
        char* finalResult = reverseString(result);
        free(reversedNum1);
        free(reversedNum2);
        free(result); // 注意:这里释放的是去除前导零之前的result内存
     
        return finalResult;
    }
    

    演示:
    在这里插入图片描述

  • 大数乘法运算

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    char* multiplyLargeNumbers(const char* num1, const char* num2);
    
    int main() {
        char num1[1001], num2[1001];
        printf("请输入第一个大数: ");
        scanf("%1000s", num1);
        printf("请输入第二个大数: ");
        scanf("%1000s", num2);
    
        char* result = multiplyLargeNumbers(num1, num2);
        printf("结果: %s\n", result);
        free(result - (result[0] == '0' && *(result + 1) == '\0')); // Adjust free pointer if leading '0' was logically removed
    
        return 0;
    }
    
    char* multiplyLargeNumbers(const char* num1, const char* num2) {
        int len1 = strlen(num1);
        int len2 = strlen(num2);
        int maxLen = len1 + len2;
        char* result = (char*)calloc(maxLen + 1, sizeof(char)); // +1 for null terminator
    
        // Initialize result array with zeros
        memset(result, '0', maxLen);
        result[maxLen] = '\0';
    
        // Perform multiplication
        for (int i = len1 - 1; i >= 0; i--) {
            for (int j = len2 - 1, k = i + j; j >= 0; j--, k--) {
                int mul = (num1[i] - '0') * (num2[j] - '0');
                int sum = mul + (result[k + 1] - '0');
                result[k + 1] = (sum % 10) + '0';
                result[k] += sum / 10;
                if (result[k] > '9') {
                    int carry = result[k] - '0' / 10;
                    result[k] = (result[k] - carry * 10) + '0';
                    if (k > 0) {
                        result[k - 1] += carry;
                    } else {
                        // Handle carry overflow, resize result if necessary
                        char* temp = (char*)realloc(result, maxLen + 2);
                        if (temp) {
                            result = temp;
                            memmove(result + 1, result, maxLen + 1);
                            result[0] = '0' + carry;
                            maxLen++;
                        } else {
                            // Allocation failed, handle error
                            free(result);
                            return NULL;
                        }
                    }
                }
            }
        }
    
        // Find the start of the non-zero part of the result
        int start = 0;
        while (start < maxLen && result[start] == '0') {
            start++;
        }
    
        // Shift the result to the left and null-terminate it
        memmove(result, result + start, maxLen - start + 1);
    
        return result;
    }
    
    

    演示:
    在这里插入图片描述

  • 大数除法及取余运算

    #include <stdio.h>
    #include <string.h>
    
    void divideLargeNumber(int *largeNumberArray, int divisionFactor, int numberOfDigits);
    
    int main()
    {
        char largeNumberStr[1000]; // 存储大整数的字符串
        int divisor; // 除数
    
        printf("请输入一个大整数(不超过999位)和一个除数(以空格分隔):");
        scanf("%s%d", largeNumberStr, &divisor);
    
        int largeNumberDigits[1000]; // 存储大整数的每一位数字
        int numberLength = strlen(largeNumberStr); // 大整数的长度
        for (int i = 0; i < numberLength; i++)
            largeNumberDigits[i] = largeNumberStr[i] - '0';
    
        divideLargeNumber(largeNumberDigits, divisor, numberLength);
    
        return 0;
    }
    
    void divideLargeNumber(int *largeNumberArray, int divisionFactor, int numberOfDigits) // 开始操作
    {
        int partialSum = 0; // 当前部分和(累加当前位及之前的数字)
        int firstDigitOutputFlag = 0; // 标记是否已输出第一个非零商位
        int remainder = 0; // 余数
    
        for (int i = 0; i < numberOfDigits; i++) // 这里没有将商存下来,而是找到一位就输出
        {
            partialSum += largeNumberArray[i];
    
            if (partialSum < divisionFactor && firstDigitOutputFlag)
            {
                remainder = partialSum; // 记录有可能最后加上最后一位还是小于divisionFactor的情况的余数
                printf("0"); // 输出商位为0的情况
            }
    
            if (partialSum >= divisionFactor)
            {
                printf("商位: %d", partialSum / divisionFactor);
                partialSum %= divisionFactor; // 更新余数
                remainder = partialSum; // 记录最后加上最后一位大于divisionFactor的情况的余数
                partialSum *= 10; // 为下一位数字做准备
                firstDigitOutputFlag = 1; // 在找到第一个商位之后改变标记状态
            }
            else
            {
                partialSum *= 10; // 如果当前部分和小于除数,则继续累加下一位数字
            }
        }
    
        printf("\n");
        printf("余数:%d\n", remainder); // 输出余数
    }
    

    演示:
    在这里插入图片描述

  • 大数阶乘运算

    #include <stdio.h>
    #include <string.h>
    #include <stdbool.h>
     
    #define MAX_DIGITS 1000 // 假设最大阶乘位数不超过1000
     
    // 辅助函数:打印大数
    void printLargeNumber(int result[], int length) {
        for (int i = length - 1; i >= 0; i--) {
            printf("%d", result[i]);
        }
        printf("\n");
    }
     
    // 大数阶乘函数
    void factorialLargeNumber(int n, int result[], int* length) {
        // 初始化结果数组为1(阶乘的初始值)
        memset(result, 0, sizeof(int) * MAX_DIGITS);
        result[0] = 1;
        *length = 1;
     
        // 计算阶乘
        for (int i = 2; i <= n; i++) {
            int carry = 0; // 进位
            for (int j = 0; j < *length; j++) {
                int product = result[j] * i + carry;
                result[j] = product % 10; // 当前位的值
                carry = product / 10;     // 进位
            }
     
            // 处理进位
            while (carry) {
                result[*length] = carry % 10;
                carry = carry / 10;
                (*length)++;
            }
        }
    }
     
    int main() {
        int n;
        printf("请输入一个整数: ");
        scanf("%d", &n);
     
        int result[MAX_DIGITS];
        int length = 0;
     
        factorialLargeNumber(n, result, &length);
     
        printf("%d! = ", n);
        printLargeNumber(result, length);
     
        return 0;
    }
    

    演示:
    在这里插入图片描述

四、小结

好了,关于大数运算的代码实现到我们这里就结束了,后面遇到其它我会在继续接着补充,希望对大家有所帮助。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java导出通过Word模板导出docx文件并通过QQ邮箱发送

一、创建Word模板 {{company}}{{Date}}服务器运行情况报告一、服务器&#xff1a;总告警次数&#xff1a;{{ServerTotal}} 服务器IP:{{IPA}}&#xff0c;总共告警次数:{{ServerATotal}} 服务器IP:{{IPB}}&#xff0c;总共告警次数:{{ServerBTotal}} 服务器IP:{{IPC}}&#x…

Linux系统编程:进程状态和进程优先级/nice

目录 一,相对于OS的进程状态 1.1运行状态 1.2阻塞状态 1.3挂起状态 二,并发执行与进程切换 2.1,CPU并发执行 2.2进程切换 三,Linux内核管理进程状态的方法 3.1查看进程状态 3.2R状态 3.3S状态 3.4D状态 3.5T状态 3.6X状态 3.7Z状态 3.8孤儿进程 四,进程优先级 …

0 基础学运维:解锁 K8s 云计算运维工程师成长密码

前言&#xff1a;作为一个过来人&#xff0c;我曾站在技术的门槛之外&#xff0c;连电脑运行内存和内存空间都傻傻分不清&#xff0c;完完全全的零基础。但如今&#xff0c;我已成长为一名资深的k8s云计算运维工程师。回顾这段历程&#xff0c;我深知踏上这条技术之路的艰辛与不…

在 vscode + cmake + GNU 工具链的基础上配置 JLINK

安装 JLINK JLINK 官网链接 下载安装后找到安装路径下的可执行文件 将此路径添加到环境变量的 Path 中。 创建 JFlash 项目 打开 JFlash&#xff0c;选择新建项目 选择单片机型号 在弹出的窗口中搜索单片机 其他参数根据实际情况填写 新建完成&#xff1a; 接下来设置…

【全栈】SprintBoot+vue3迷你商城(9)

【全栈】SprintBootvue3迷你商城&#xff08;9&#xff09; 往期的文章都在这里啦&#xff0c;大家有兴趣可以看一下 后端部分&#xff1a; 【全栈】SprintBootvue3迷你商城&#xff08;1&#xff09; 【全栈】SprintBootvue3迷你商城&#xff08;2&#xff09; 【全栈】Spr…

省市区三级联动

引言 在网页中&#xff0c;经常会遇到需要用户选择地区的场景&#xff0c;如注册表单、地址填写等。为了提供更好的用户体验&#xff0c;我们可以实现一个三级联动的地区选择器&#xff0c;让用户依次选择省份、城市和地区。 效果展示&#xff1a; 只有先选择省份后才可以选择…

Fullcalendar @fullcalendar/react 样式错乱丢失问题和导致页面卡顿崩溃问题

问题描述&#xff1a; 我使用 fullcalendar的react版本时&#xff0c;出现了一个诡异的问题&#xff0c;当我切换到 一个iframe页面时&#xff08;整个页面是一个iframe嵌入的&#xff09;&#xff0c;再切换回来日历的样式丢失了&#xff01;不仅丢失了样式还导致页面崩溃了&…

dm8在Linux环境安装精简步骤说明(2024年12月更新版dm8)

dm8在Linux环境安装详细步骤 - - 2025年1月之后dm8 环境介绍1 修改操作系统资源限制2 操作系统创建用户3 操作系统配置4 数据库安装5 初始化数据库6 实例参数优化7 登录数据库配置归档与备份8 配置审计9 创建用户10 屏蔽关键字与数据库兼容模式11 jdbc连接串配置12 更多达梦数据…

S4 HANA更改Tax base Amount的字段控制

本文主要介绍在S4 HANA OP中Tax base Amount的字段控制相关设置。具体请参照如下内容&#xff1a; 1. 更改Tax base Amount的字段控制 以上配置用于控制FB60/FB65/FB70/FB75/MIRO的页签“Tax”界面是否可以修改“Tax base Amount”&#xff0c; 如果勾选Change 表示可以修改T…

JVM堆空间

一、堆空间的核心概述 一个JVM实例只存在一个堆内存&#xff0c;堆也是Java内存管理的核心区域。Java堆区在JVM启动的时候即被创建&#xff0c;其空间大小也就确定了。是JVM管理的最大一块内存空间。 堆内存的大小是可以调节的。堆可以处于物理上不连续的内存空间中&#xff…

《深入解析:DOS检测的技术原理与方法》

DDOS入侵检测与防御 一、实现Linux下DDOS的入侵检测与防御 利用Python编程实现对wrk的泛洪攻击检测&#xff0c;并让程序触发调用Linux命令实现防御: 1、泛洪攻击的检测&#xff0c;可以考虑使用的命令&#xff0c;这些命令可以通过Python进行调用和分析 (1) netstat -ant …

PID如何调试,如何配置P,I,D值,如何适配pwm的定时器配置,如何给小车配电源

首先你要搞清楚PID公式原理 PID算法解析PID算法解析_pid滤波算法-CSDN博客 然后你要明白调试原理 首先要确定一个电源 电源决定了你后面调试时电机转动速度大小和pwm占空比的关系&#xff0c;电源电压越大那要转到同一速度所需的占空比越小&#xff0c;反之电源电压越小那要…

小马模拟器-第三方全街机游戏模拟器

链接&#xff1a;https://pan.xunlei.com/s/VOHSiB6st-f3RWlIK01MS2fUA1?pwd44v7# 1.小马模拟器是一款完全免费的游戏模拟器软件&#xff0c;支持街机&#xff08;FBA,MAME,PGM2&#xff09;,3DS,WII,NGC,DC,SS,DOS,MD,WSC,NDS,JAVA,PCE,FC,SFC,GBA,GBC,PSP,PS,N64等多种游戏…

微信小程序date picker的一些说明

微信小程序的picker是一个功能强大的组件&#xff0c;它可以是一个普通选择器&#xff0c;也可以是多项选择器&#xff0c;也可以是时间、日期、省市区选择器。 官方文档在这里 这里讲一下date picker的用法。 <view class"section"><view class"se…

【算法】递归型枚举与回溯剪枝初识

递归型枚举与回溯剪枝初识 1.枚举子集2.组合型枚举3.枚举排列4.全排列问题 什么是搜索&#xff1f;搜索&#xff0c;是一种枚举&#xff0c;通过穷举所有的情况来找到最优解&#xff0c;或者统计合法解的个数。因此&#xff0c;搜索有时候也叫作暴搜。搜索一般分为深度优先搜索…

rocketmq-product-send方法源码分析

先看有哪些send方法 首先说红圈的 有3个红圈。归类成3种发送方式。假设前提条件&#xff0c;发送的topic&#xff0c;有3个broker&#xff0c;每个broker总共4个write队列&#xff0c;总共有12个队列。 普通发送。负载均衡12个队列。指定超时时间指定MessageQueue,发送&#…

69.在 Vue 3 中使用 OpenLayers 拖拽实现放大区域的效果(DragPan)

引言 在现代 Web 开发中&#xff0c;地图功能已经成为许多应用的重要组成部分。OpenLayers 是一个功能强大的开源地图库&#xff0c;支持多种地图源和交互操作。Vue 3 是一个流行的前端框架&#xff0c;以其响应式数据和组件化开发著称。本文将介绍如何在 Vue 3 中集成 OpenLa…

77,【1】.[CISCN2019 华东南赛区]Web4

有句英文&#xff0c;看看什么意思 好像也可以不看 进入靶场 点击蓝色字体 我勒个豆&#xff0c;百度哇 所以重点应该在url上&#xff0c;属于任意文件读取类型 接下来该判断框架了 常见的web框架如下 一&#xff0c;Python 框架 1.Flask URL 示例 1&#xff1a;http://…

手撕B-树

一、概述 1.历史 B树&#xff08;B-Tree&#xff09;结构是一种高效存储和查询数据的方法&#xff0c;它的历史可以追溯到1970年代早期。B树的发明人Rudolf Bayer和Edward M. McCreight分别发表了一篇论文介绍了B树。这篇论文是1972年发表于《ACM Transactions on Database S…

一文简单回顾复习Java基础概念

还是和往常一样&#xff0c;我以提问的方式回顾复习&#xff0c;今天回顾下Java小白入门应该知道的一些基础知识 Java语言有哪些特点呢&#xff1f; Java语言的特点有&#xff1a; 面向对象&#xff0c;主要是封装、继承、多态&#xff1b;平台无关性&#xff0c;“一次编写…