1、大数加法
两个大数我们可以用数组来保存,然后在数组中逐位进行相加,再判断该位相加后是否需要进位,为了方便计算,我们将数字的低位放在数组的前面,高位放在后面。
下面是两个正的大整数相加算法的C语言参考代码:
#include<stdio.h>
#include<string.h>
#define MAX 1000 // 大数的最大位数
/*
大数加法
参数:
num1为第一个大数,用字符数组保存
num2为第二个大数
sum数组保存相加的结果 即:num1+num2=sum
返回值:返回数组sum的有效长度,即计算结果的位数
*/
int Addition(char num1[], char num2[], int sum[])
{
int i, j, len;
int n2[MAX] = {0};
int len1 = strlen (num1); // 计算数组num1的长度,即大数的位数
int len2 = strlen (num2); // 计算数组num2的长度,即大数的位数
len = len1>len2 ? len1 : len2; // 获取较大的位数
//将num1字符数组的数字字符转换为整型数字,且逆向保存在整型数组sum中,即低位在前,高位在后
for (i = len1-1, j = 0; i >= 0; i--, j++)
sum[j] = num1[i] - '0';
// 转换第二个数
for (i = len2-1, j = 0; i >= 0; i--, j++)
n2[j] = num2[i] - '0';
// 将两个大数相加
for (i = 0; i <= len; i++)
{
sum[i] += n2[i]; // 两个数从低位开始相加
if (sum[i] > 9) // 判断是否有进位
{ // 进位
sum[i] -= 10;
sum[i+1]++;
}
}
if(sum[len] != 0) // 判断最高位是否有进位
len++;
return len; // 返回和的位数
}
int main()
{
int i, len;
int sum[MAX] = {0}; // 存放计算的结果,低位在前,高位在后,即sum[0]是低位
char num1[] = "1234567891234567891234"; // 第一个大数
char num2[] = "2345678912345678913345"; // 第二个大数
len = Addition(num1, num2, sum); // 两数相加
printf("%s\n +\n%s\n =\n", num1, num2);
// 反向输出求和结果
for (i = len-1; i >= 0; i--)
printf("%d", sum[i]);
printf("\n");
return 0;
}
2、大数减法
相减算法也是从低位开始减的。先要判断被减数和减数哪一个位数长,若被减数位数长是正常的减法;若减数位数长,则用被减数减去减数,最后还要加上负号;当两数位数长度相等时,最好比较哪一个数字大,否则负号处理会很繁琐;处理每一项时要,如果前一位相减有借位,就先减去上一位的借位,无则不减,再去判断是否能够减开被减数,如果减不开,就要借位后再去减,同时置借位为1,否则置借位为0。
下面是C语言参考代码:
#include<stdio.h>
#include<string.h>
#define MAX 1000 // 大数的最大位数
/*
大数减法
参数:
num1为被减数,用字符数组保存
num2为减数
sum数组保存相减的结果 即:num1-num2=sum
返回值:返回数组sum的有效长度,即计算结果的位数
*/
int Subtraction(char num1[], char num2[], int sum[])
{
int i, j, len, blag;
char *temp;
int n2[MAX] = {0};
int len1 = strlen(num1); // 计算数组num1的长度,即大数的位数
int len2 = strlen(num2); // 计算数组num2的长度,即大数的位数
// 在进行减法之前要进行一些预处理
blag = 0; // 为0表示结果是正整数,为1表示结果是负整数
if(len1 < len2) // 如果被减数位数小于减数
{
blag = 1; // 标记结果为负数
// 交换两个数,便于计算
temp = num1;
num1 = num2;
num2 = temp;
len = len1;
len1 = len2;
len2 = len;
}
else if(len1 ==len2) // 如果被减数的位数等于减数的位数
{
// 判断哪个数大
for(i = 0; i < len1; i++)
{
if(num1[i] == num2[i])
continue;
if(num1[i] > num2[i])
{
blag = 0; // 标记结果为正数
break;
}
else
{
blag = 1; // 标记结果为负数
// 交换两个数,便于计算
temp = num1;
num1 = num2;
num2 = temp;
break;
}
}
}
len = len1>len2 ? len1 : len2; // 获取较大的位数
//将num1字符数组的数字转换为整型数且逆向保存在整型数组sum中,即低位在前,高位在后
for (i = len1-1, j = 0; i >= 0; i--, j++)
sum[j] = num1[i] - '0';
// 转换第二个数
for (i = len2-1, j = 0; i >= 0; i--, j++)
n2[j] = num2[i] - '0';
// 将两个大数相减
for (i = 0; i <= len; i++)
{
sum[i] = sum[i] - n2[i]; // 两个数从低位开始相减
if (sum[i] < 0) // 判断是否有借位
{ // 借位
sum[i] += 10;
sum[i+1]--;
}
}
// 计算结果长度
for (i = len1-1; i>=0 && sum[i] == 0; i--)
;
len = i+1;
if(blag==1)
{
sum[len] = -1; // 在高位添加一个-1表示负数
len++;
}
return len; // 返回结果的位数
}
int main()
{
int i, len;
int sum[MAX] = {0}; // 存放计算的结果,低位在前,高位在后,即sum[0]是低位
char num1[] = "987654321987654321"; // 第一个大数
char num2[] = "123456789123456789"; // 第二个大数
len = Subtraction(num1, num2, sum); // 两数相减
// 输出结果
printf("%s\n -\n%s\n =\n", num1, num2);
if(sum[i=len-1] < 0) // 根据高位是否是-1判断是否是负数
{
printf("-"); // 输出负号
i--;
}
for (; i >= 0; i--)
printf("%d", sum[i]);
printf("\n");
return 0;
}
3、大数乘法
首先说一下乘法计算的算法,从低位向高位乘,在竖式计算中,我们是将乘数第一位与被乘数的每一位相乘,记录结果,之后,用第二位相乘,记录结果并且左移一位,以此类推,直到计算完最后一位,再将各项结果相加,得出最后结果。
计算的过程基本上和小学生列竖式做乘法相同。为了编程方便,并不急于处理进位,而是将进位问题留待最后统一处理。
总结一个规律: 即一个数的第i 位和另一个数的第j 位相乘所得的数,一定是要累加到结果的第i+j 位上。这里i, j 都是从右往左,从0 开始数。
ans[i+j] = a[i]*b[j];
另外注意进位时要处理,当前的值加上进位的值再看本位数字是否又有进位;前导清零。
下面是C语言的两个正大数相乘的参考代码:
#include<stdio.h>
#include<string.h>
#define MAX 1000 // 大数的最大位数
/*
大数乘法
参数:
num1为第一个因数,用字符数组保存
num2为第二个因数
sum数组保存相乘的结果 即:num1*num2=sum
返回值:返回数组sum的有效长度,即计算结果的位数
*/
int Multiplication(char num1[],char num2[], int sum[])
{
int i, j, len, len1, len2;
int a[MAX+10] = {0};
int b[MAX+10] = {0};
int c[MAX*2+10] = {0};
len1 = strlen(num1);
for(j = 0, i = len1-1; i >= 0; i--) //把数字字符转换为整型数
a[j++] = num1[i]-'0';
len2 = strlen(num2);
for(j = 0, i = len2-1; i >= 0; i--)
b[j++] = num2[i]-'0';
for(i = 0; i < len2; i++)//用第二个数乘以第一个数,每次一位
{
for(j = 0; j < len1; j++)
{
c[i+j] += b[i] * a[j]; //先乘起来,后面统一进位
}
}
for(i=0; i<MAX*2; i++) //循环统一处理进位问题
{
if(c[i]>=10)
{
c[i+1]+=c[i]/10;
c[i]%=10;
}
}
for(i = MAX*2; c[i]==0 && i>=0; i--); //跳过高位的0
len = i+1; // 记录结果的长度
for(; i>=0; i--)
sum[i]=c[i];
return len;
}
int main()
{
int i, len;
int sum[MAX*2+10] = {0}; // 存放计算的结果,低位在前,高位在后,即sum[0]是低位
char num1[] = "123456789123456789"; // 第一个大数
char num2[] = "123456789123456789"; // 第二个大数
len = Multiplication(num1, num2, sum);
// 输出结果
printf("%s\n *\n%s\n =\n", num1, num2);
for(i = len-1; i>=0; i--)
printf("%d", sum[i]);
printf("\n");
return 0;
}
4、大数除法
大数除法是四则运算里面最难的一种。不同于一般的模拟,除法操作不是模仿手工除法,而是利用减法操作来实现的。其基本思想是反复做除法,看从被除数里面最多能减去多少个除数,商就是多少。逐个减显然太慢,要判断一次最多能减少多少个整数(除数)的10的n次方。
以7546除以23为例:
先用7546减去23的100倍,即减去2300,可以减3次,余下646,此时商就是300 (300=100*3);
然后646减去23的10倍,即减去230,可以减2次,余下186,此时商就是320 (320=300+10*2);
然后186减去23,可以减8次,余下2,此时商就是328 (328=320+1*8);
因为2除以23的结果小于1,而我们又不用计算小数点位,所以不必再继续算下去了。
下面是C语言的两个正大数相除的参考代码,计算结果中没有小数:
#include<stdio.h>
#include<string.h>
#define MAX 1000 // 大数的最大位数
// 注:
// 本代码在以下博客代码中进行修改:
// http://www.cnblogs.com/javawebsoa/archive/2013/08/01/3231078.html
//
/*
函数SubStract功能:
用长度为len1的大整数p1减去长度为len2的大整数p2
结果存在p1中,返回值代表结果的长度
不够减:返回-1 , 正好够:返回0
*/
int SubStract(int *p1, int len1, int *p2, int len2)
{
int i;
if(len1 < len2)
return -1;
if(len1 == len2 )
{ // 判断p1 > p2
for(i = len1-1; i >= 0; i--)
{
if(p1[i] > p2[i]) // 若大,则满足条件,可做减法
break;
else if(p1[i] < p2[i]) // 否则返回-1
return -1;
}
}
for(i = 0; i <= len1-1; i++) // 从低位开始做减法
{
p1[i] -= p2[i]; // 相减
if(p1[i] < 0) // 若是否需要借位
{ // 借位
p1[i] += 10;
p1[i+1]--;
}
}
for(i = len1-1; i >= 0; i--) // 查找结果的最高位
{
if( p1[i] ) //最高位第一个不为0
return (i+1); //得到位数并返回
}
return 0; //两数相等的时候返回0
}
/*
大数除法---结果不包括小数点
num1 被除数
num2 除数
sum 商,存放计算的结果,即:num1/num2=sum
返回数组sum的有效长度,即商的位数
*/
int Division(char num1[], char num2[], char sum[])
{
int k, i, j;
int len1, len2, len=0; //大数位数
int dValue; //两大数相差位数
int nTemp; //Subtract函数返回值
int num_a[MAX] = {0}; //被除数
int num_b[MAX] = {0}; //除数
int num_c[MAX] = {0}; //商
len1 = strlen(num1); //获得大数的位数
len2 = strlen(num2);
//将数字字符转换成整型数,且翻转保存在整型数组中
for( j = 0, i = len1-1; i >= 0; j++, i-- )
num_a[j] = num1[i] - '0';
for( j = 0, i = len2-1; i >= 0; j++, i-- )
num_b[j] = num2[i] - '0';
if( len1 < len2 ) //如果被除数小于除数,直接返回-1,表示结果为0
{
return -1;
}
dValue = len1 - len2; //相差位数
for (i = len1-1; i >= 0; i--) //将除数扩大,使得除数和被除数位数相等
{
if (i >= dValue)
num_b[i] = num_b[i-dValue];
else //低位置0
num_b[i] = 0;
}
len2 = len1;
for(j = 0; j <= dValue; j++ ) //重复调用,同时记录减成功的次数,即为商
{
while((nTemp = SubStract(num_a, len1, num_b+j, len2-j)) >= 0)
{
len1 = nTemp; //结果长度
num_c[dValue-j]++; //每成功减一次,将商的相应位加1
}
}
// 计算商的位数,并将商放在sum字符数组中
for(i = MAX-1; num_c[i] == 0 && i >= 0; i-- ); //跳过高位0,获取商的位数
if(i >= 0)
len = i + 1; // 保存位数
for(j = 0; i >= 0; i--, j++) // 将结果复制到sum数组中
sum[j] = num_c[i] + '0';
sum[j] = '\0'; // sum字符数组结尾置0
return len; // 返回商的位数
}
int main()
{
int i;
int len; // 商的位数
char num1[MAX] = "1234567899876543210"; // 第一个大数
char num2[MAX] = "20160415123025"; // 第二个大数
char sum[MAX] = {0}; // 计算结果
//scanf("%s", num1); //以字符串形式读入大数
//scanf("%s", num2);
len = Division(num1, num2, sum);
//输出结果
printf("%s\n ÷\n%s\n =\n", num1, num2);
if( len>=0 )
{
for(i = 0; i < len; i++ )
printf("%c", sum[i]);
}
else
{
printf("0");
}
printf("\n");
return 0;
}
5、使用Java提供的类
在Java中提供了BigInteger类和BigDecimal类,分别用来处理大整数和大浮点数,我们只要调用里面提供的方法就能很方便的进行大数的四则运算,具体实现可参考:
点击打开链接
Java大数处理类:BigInteger类和BigDecimal类
当我们要处理非常大的数据时,平常用的数据类型已不足以表示,在Java中有两个类BigInteger和BigDecimal分别表示大整数类和大浮点数类,这两个类在理论上
只要计算机内存足够大就
能够表示无线大的数。
它们都在java.math.*包中,我们可以在API文档中进行查看
import java.math.BigDecimal;
import java.math.BigInteger;
/**
* 测试大数
*/
public class BigNumberDemo {
/**
* 测试 BigInteger大整数类
*/
private static void testBigInteger() {
BigInteger bigInt1 = new BigInteger("12345678912345678912345"); // 实例化一个 BigInteger
long ln = 1234567899876543210L;
BigInteger bigInt2 = BigInteger.valueOf(ln); // 返回其值等于指定long的值的 BigInteger
System.out.println("测试BigInteger类:");
System.out.println("bigInt1 = " + bigInt1.toString()); // 输出值
System.out.println("bigInt2 = " + bigInt2.toString());
// 加法运算
BigInteger sum = bigInt1.add(bigInt2); // 返回值为(bigInt1+bigInt2)的 BigInteger
System.out.println("bigInt1 + bigInt2 = " + sum.toString());
// 减法运算
sum = bigInt1.subtract(bigInt2); // 返回值为(bigInt1-bigInt2)的 BigInteger
System.out.println("bigInt1 - bigInt2 = " + sum.toString());
// 乘法运算
sum = bigInt1.multiply(bigInt2); // 返回值为(bigInt1*bigInt2)的 BigInteger
System.out.println("bigInt1 * bigInt2 = " + sum.toString());
// 除法运算:整数商
sum = bigInt1.divide(bigInt2); // 返回值为(bigInt1/bigInt2)整数结果的 BigInteger
System.out.println("bigInt1 / bigInt2 = " + sum.toString());
// 除法运算:商+余数
BigInteger[] sums = bigInt1.divideAndRemainder(bigInt2); // 返回值为(bigInt1/bigInt2)整数结果的 BigInteger
System.out.println("bigInt1 / bigInt2 = " + sums[0].toString()
+ "\t余数:bigInt1 % bigInt2 = " + sums[1].toString());
// 取余数
sum = bigInt1.mod(bigInt2);
System.out.println("bigInt1 % bigInt2 = " + sum.toString());
// 指数
sum = bigInt1.pow(2);
System.out.println("bigInt1 ^ 2 = " + sum.toString());
// 比较两个数是否相等
System.out.println("比较两个数是否相等: " + bigInt1.equals(bigInt2));
// 比较两个数的大小
System.out.println("比较两个数的大小: " + bigInt1.compareTo(bigInt2)
+ " (若小于为-1,等于为0,大于为1)");
}
/**
* 测试 BigDecimal大浮点数类
*/
private static void testBigDecimal() {
BigDecimal bigDec1 = new BigDecimal("1234512345678912345678912345.123"); // 实例化一个 BigDecimal
Double db = 123456789.123456D;
BigDecimal bigDec2 = BigDecimal.valueOf(db); // 返回其值等于指定Double的值的 BigDecimal
System.out.println("\n测试BigDecimal类:");
System.out.println("bigDec1 = " + bigDec1); // 输出值
System.out.println("bigDec2 = " + bigDec2.toString());
// 加法运算
BigDecimal sum = bigDec1.add(bigDec2); // 返回值为(bigDec1+bigDec2)的 BigDecimal
System.out.println("bigDec1 + bigDec2 = " + sum.toString());
// 减法运算
sum = bigDec1.subtract(bigDec2); // 返回值为(bigDec1-bigDec2)的 BigDecimal
System.out.println("bigDec1 - bigDec2 = " + sum.toString());
// 乘法运算
sum = bigDec1.multiply(bigDec2); // 返回值为(bigDec1*bigDec2)的 BigDecimal
System.out.println("bigDec1 * bigDec2 = " + sum.toString());
// 除法运算:整数商
sum = bigDec1.divideToIntegralValue(bigDec2); // 返回值为(bigDec1/bigDec2)整数结果的 BigDecimal
System.out.println("bigDec1 / bigDec2 = " + sum.toString());
// 除法运算:商+余数
BigDecimal[] sums = bigDec1.divideAndRemainder(bigDec2); // 返回值为(bigDec1/bigDec2)整数结果的 BigDecimal
System.out.println("bigDec1 / bigDec2 = " + sums[0].toString()
+ "\t余数:bigDec1 % bigDec2 = " + sums[1].toString());
// 取余数
sum = bigDec1.remainder(bigDec2);
System.out.println("bigDec1 % bigDec2 = " + sum.toString());
// 指数
sum = bigDec1.pow(2);
System.out.println("bigDec1 ^ 2 = " + sum.toString());
// 比较两个数是否相等
System.out.println("比较两个数是否相等: " + bigDec1.equals(bigDec2));
// 比较两个数的大小
System.out.println("比较两个数的大小: " + bigDec1.compareTo(bigDec2)
+ " (若小于为-1,等于为0,大于为1)");// 末位数据精度
System.out.println("bigDec1的末位数据精度: " + bigDec1.ulp());
}
public static void main(String[] args) {
testBigInteger();
testBigDecimal();
}
}