整数浮点数通用大数精确算法
加减乘除法大数算法单头文件库。
具体用法看源文件中的调用以及解释。
源文件
#include<stdio.h>
#define WGX_MATH_CALC_IMPLEMENTATION
#include"wgx_math_calc.h"
int main(int argc, char** argv){
char res[20000];
char s1[10000];
char s2[10000];
char *p=NULL;
printf("输入被除数:\n");
scanf("%9999s",s1);
printf("\n\n");
printf("输入除数:\n");
scanf("%9999s",s2);
//注意下面参数这个500是精度,必须为正整数
p=wgx_div(s1,s2,1000);
printf("\n\n%s\n除以\n%s\n=\n%s",s1,s2,p);
free(p);
return 0;
}
下面是头文件
wgx_math_calc.h
// wgx_math_calc.h文件
// 加减乘除超大数计算
#ifndef INCLUDE_WGX_MATH_CALC_H
#define INCLUDE_WGX_MATH_CALC_H
#ifdef __cplusplus
extern "C"
{
#endif
// 字符串形式比较两个数大小
extern int s1_compare_s2(char *s1, char *s2);
// 求正整数的倒数
extern char *daoshu(char *fenmu, unsigned int maxlen);
// 去掉无效0,用完记得释放指针
extern char *delInvalidZeros(char *str);
// 大数加法算法
extern char *wgx_add(char *s1, char *s2);
// 大数减法算法
extern char *wgx_sub(char *s1, char *s2);
// 大数乘法算法
extern char *wgx_mcl(char *s1, char *s2);
// 大数除法算法
extern char *wgx_div(char *s1, char *s2, unsigned int maxlen);
// 大数阶乘算法
// m的n次方算法
#ifdef __cplusplus
}
#endif
#ifdef WGX_MATH_CALC_IMPLEMENTATION
#ifndef STDIO_H
#include<stdio.h>
#endif
#ifndef STDLIB_H
#include<stdlib.h>
#endif
#ifndef STRING_H
#include<string.h>
#endif
#ifndef MALLOC_H
#include<malloc.h>
#endif
#ifndef CTYPE_H
#include<ctype.h>
#endif
#ifndef MATH_H
#include<math.h>
#endif
// 用字符串形式比较两个超大数(兼容整数和小数)的大小
// s1>s2返回1 s1<s2返回-1 s1=s2返回0
int s1_compare_s2(char *s1, char *s2)
{
if (s1 == NULL || s2 == NULL)
return -2;
// 首先获取s1和s2的长度
unsigned int s1len = strlen(s1), s2len = strlen(s2);
// s1和s2对齐后的长度相等
unsigned int len = 0;
// 统计小数点的个数
unsigned int num = 0;
// s1 和 s2 的小数标记 是小数赋值1 否则0
int sign1 = 0, sign2 = 0;
// 小数点位置索引
unsigned int index1 = 0, index2 = 0;
// 非法字符退出
for (int i = 0; i < s1len; i++)
{
if (*(s1 + i) == '.')
{
num++;
sign1 = 1;
index1 = i;
}
if ((!isdigit(*(s1 + i)) && *(s1 + i) != '.') || num > 1)
return -2;
}
// 重置num值
num = 0;
// 非法字符退出
for (int i = 0; i < s2len; i++)
{
if (*(s2 + i) == '.')
{
num++;
sign2 = 1;
index2 = i;
}
if ((!isdigit(*(s2 + i)) && *(s2 + i) != '.') || num > 1)
return -2;
}
// ------------------------------
int len1 = 0;
// s1整数部分长度
// 计算len1的长度
if (sign1 == 0)
{
len1 = s1len;
// 是整数
}
else if (sign1 == 1)
{
len1 = index1;
// 是小数
}
int len2 = 0;
// s2整数部分长度
// 计算len2的长度
if (sign2 == 0)
{
len2 = s2len;
// 是整数
}
else if (sign2 == 1)
{
len2 = index2;
// 是小数
}
// 计算对齐总长度len
len =
(len1 > len2 ? len1 : len2) + ((s1len - len1) >
(s2len - len2) ? (s1len - len1) : (s2len - len2));
// 动态分配内存 存放对齐后的s1 和s2
char *ps1 = (char *)malloc(sizeof(char) * len);
char *ps2 = (char *)malloc(sizeof(char) * len);
// 初始化指针所指的内存区域
memset(ps1, '0', len);
memset(ps2, '0', len);
// s1和s2复制到内存指针中
for (int i = 0; i < s1len; i++)
{
// 遇到小数点 把其替换成'0' 反正不影响判断结果
// 这个特殊'0'的位置索引前面已经被标记过了
if (*(s1 + i) == '.')
{
*(ps1 + i) = '0';
}
else
{
*(ps1 + i) = *(s1 + i);
}
}
for (int i = 0; i < s2len; i++)
{
// 遇到小数点 把其替换成'0' 反正不影响判断结果
// 这个特殊'0'的位置索引前面已经被标记过了
if (*(s2 + i) == '.')
{
*(ps2 + i) = '0';
}
else
{
*(ps2 + i) = *(s2 + i);
}
}
// 向右平移对齐 平移单位n
int n = 0;
n = abs(len1 - len2);
char *ps1_1 = ps1 + n;
char *ps2_1 = ps2 + n;
if (len1 > len2)
{
// s2整体右移动n位 源ps2 目标ps2_1
memmove(ps2_1, ps2, s2len);
// 腾出位置用'0'填充
for (int i = 0; i < n; i++)
{
*(ps2 + i) = '0';
}
}
else if (len1 < len2)
{
// s1整体右移动n位 源ps1 目标ps1_1
memmove(ps1_1, ps1, s1len);
// 腾出位置用'0'填充
for (int i = 0; i < n; i++)
{
*(ps1 + i) = '0';
}
}
// 开始比较从低索引开始
for (int i = 0; i < len; i++)
{
if (*(ps1 + i) > *(ps2 + i))
return 1;
// s1大于s2返回1
if (*(ps1 + i) < *(ps2 + i))
return -1;
// s1小于s2返回-1
}
free(ps1);
free(ps2);
return 0;
}
char *daoshu(char *fenmu, unsigned int maxlen)
{
// 分母(除数)有效字串
char *fenmu0 = delInvalidZeros(fenmu);
if (!fenmu0)
return 0;
// 分母长度fmlen
unsigned int fmlen = strlen(fenmu0);
// 存放分母的0到9倍数据
char *fmtimes[10];
// 商放置区,最大长度maxlen
char *shang = NULL;
shang = (char *)calloc(sizeof(char), (maxlen + 1));
if (!shang)
return 0;
memset(shang, '\0', maxlen + 1);
// 分子(被除数)缓冲区,长度分母长度+1'\0'
char *fenzi_buf = (char *)calloc(sizeof(char), fmlen + 2);
if (!fenzi_buf)
return 0;
memset(fenzi_buf, '\0', fmlen + 2);
fenzi_buf[0] = '1';
// 求分母0-9倍的值
fmtimes[0] = "0";
for (int i = 1; i < 10; i++)
{
char stmp[2];
stmp[0] = i + '0';
stmp[1] = '\0';
fmtimes[i] = delInvalidZeros(wgx_mcl(fenmu0, stmp));
}
// 比较分子分母大小信号
int sign;
int s_i = 0;
// 商递增
int fz_i = 0;
// 分子递增 "1\0"
int fz_len = 2;
// 分子长度
int sign1 = 0;
// 信号
unsigned int n = maxlen;
// 总循环次数
while (fz_i < maxlen && n--)
{
sign = s1_compare_s2(fenzi_buf, fenmu0);
if (sign == 0)
{
strncat(shang, "1", 2);
memmove(shang + 2, shang + 1, strlen(shang) - 1);
*(shang + 1) = '.';
return shang;
}
else if (sign == -1)
{
strncat(shang, "0", 2);
strncat(fenzi_buf, "0", 2);
}
else if (sign == 1)
{
break;
}
}
char tmp[2];
while (n--)
{
sign1 = s1_compare_s2(fenzi_buf, fenmu0);
int i;
if (sign1 == 1)
{
for (i = 9; i > 0; i--)
{
sign = s1_compare_s2(fmtimes[i], fenzi_buf);
if (sign == 0 || sign == -1)
{
tmp[0] = i + '0';
tmp[1] = '\0';
strncat(shang, tmp, 2);
if (sign == 0)
{
memmove(shang + 2, shang + 1, strlen(shang) - 1);
*(shang + 1) = '.';
return shang;
}
strcpy(fenzi_buf, delInvalidZeros(wgx_sub(fenzi_buf, fmtimes[i])));
strncat(fenzi_buf, "0", 2);
break;
}
}
}
else if (sign1 == 0)
{
strncat(shang, "1", 2);
memmove(shang + 2, shang + 1, strlen(shang) - 1);
*(shang + 1) = '.';
return shang;
}
else if (sign1 == -1)
{
strncat(shang, "0", 2);
strncat(fenzi_buf, "0", 2);
}
}
// 01
memmove(shang + 2, shang + 1, strlen(shang) - 1);
*(shang + 1) = '.';
free(fenmu0);
free(fenzi_buf);
return shang;
}
// 去掉无效0,用完记得释放指针
char *delInvalidZeros(char *str)
{
int len = strlen(str);
// if(len==1)return str;
int flag = 0;
// 小数标记符号
char c = '.';
char *valStr = (char *)calloc(sizeof(char), (len + 1));
if (!valStr)
return NULL;
if (strchr(str, c) != NULL)
{
flag = 1;
}
int i_index = 0;
// 整数时第一个非零索引
int j_index = 0;
// 小数首索引
int k_index = 0;
// 小数尾索引
if (flag == 0)
{
for (int i = 0; *(str + i) != '\0'; i++)
{
if (*(str + i) != '0')
{
i_index = i;
break;
}
}
}
else if (flag == 1)
{
// 尾索引
for (int i = len - 1; i >= 0; i--)
{
if (*(str + i) == '.')
{
k_index = i - 1;
break;
}
else if (*(str + i) != '0')
{
k_index = i;
break;
}
}
// 首索引
for (int i = 0; i < len; i++)
{
if (*(str + i + 1) == '.')
{
j_index = i;
break;
}
else if (*(str + i) != '0')
{
j_index = i;
break;
}
}
}
// 把有效字串复制到valStr
if (flag == 1)
memcpy(valStr, (str + j_index), 1 + k_index - j_index);
else if (flag == 0)
memcpy(valStr, str + i_index, len - i_index);
return valStr;
}
// 加法算法
char *wgx_add(char *s1, char *s2)
{
int s1len = strlen(s1);
int s2len = strlen(s2);
int len1 = 0, len2 = 0;
// 对拷s1和s2并转换成数字后补齐位数后的长度,若是小数其实len1和len2的长度是一样的
int count1 = 0, count11 = 0;
// 分别是第一个数的小数部分长度 和 整数部分长度
int F1 = 0;
// 用于判断是否是小数,是小数F1=1,否则F1=0
for (int i = 0; i < s1len; i++)
{
if (*(s1 + i) == '.')
{
F1 = 1;
break;
}
}
// 若是小数则 计算小数部分长度
for (int i = s1len - 1; i >= 0 && (F1 == 1); i--)
{
if (*(s1 + i) != '.')
count1++;
else
break;
}
// 计算第一个加数的整数部分长度
count11 = (F1 == 1) ? (s1len - 1 - count1) : s1len;
// 相同方法处理第二个加数
int count2 = 0, count22 = 0;
int F2 = 0;
for (int i = 0; i < s2len; i++)
{
if (*(s2 + i) == '.')
{
F2 = 1;
break;
}
}
for (int i = s2len - 1; i >= 0 && (F2 == 1); i--)
{
if (*(s2 + i) != '.')
count2++;
else
break;
}
count22 = (F2 == 1) ? (s2len - 1 - count2) : s2len;
len1 = (count1 > count2) ? count1 : count2;
int cc = len1;
// 用于后面标记小数的位置,这几行语句顺序不能颠倒
len1 += (count11 > count22) ? count11 : count22;
len2 = len1;
// 为两个加数定义为a和b并为其动态分配内存空间
int *a = (int *)malloc(sizeof(int) * len1);
int *b = (int *)malloc(sizeof(int) * len2);
if (!a || !b)
return "error";
// 初始化a和b len1和len2其实是一样大的
for (int i = 0; i < len1; i++)
{
*(a + i) = 0;
*(b + i) = 0;
}
// 定义和变量 以及取得长度
int len = len1 + 1;
int *sum = (int *)malloc(sizeof(int) * len);
if (!sum)
return "error";
// 初始化和变量
for (int i = 0; i < len; i++)
*(sum + i) = 0;
int tt1 = count1 - count2;
// 用计算补齐几个0用
for (int i = s1len - 1, j = len1 - 1; j >= 0; i--, j--)
{
if (tt1 < 0)
{
while (tt1)
{
*(a + j--) = 0;
tt1++;
}
}
if (*(s1 + i) == '.')
i--;
if (i >= 0)
*(a + j) = *(s1 + i) - '0';
else
*(a + j) = 0;
}
int tt2 = count2 - count1;
// 用计算补齐几个0用
for (int i = s2len - 1, j = len2 - 1; j >= 0; i--, j--)
{
if (tt2 < 0)
{
while (tt2)
{
*(b + j--) = 0;
tt2++;
}
}
if (*(s2 + i) == '.')
i--;
if (i >= 0)
*(b + j) = *(s2 + i) - '0';
else
*(b + j) = 0;
}
int cou = 0;
for (int i = len - 1, j = len1 - 1, k = len2 - 1; i >= 0; i--, j--, k--)
{
cou++;
if (cou <= len1)
{
*(sum + i) = *(a + j) + *(b + k);
}
else
*(sum + i) = 0;
}
int temp1 = 0;
for (int i = len - 1; i >= 0; i--)
{
temp1 = *(sum + i);
*(sum + i) = *(sum + i) % 10;
if (i > 0)
*(sum + i - 1) += temp1 / 10;
}
// 输出
char *sumres = (char *)malloc(sizeof(char) * (len + 1));
if (!sumres)
return "error!";
memset(sumres, '0', len + 1);
int i, j;
for (i = 0, j = 0; i < len; i++, j++)
{
if ((F1 || F2) && (i == (len - cc)))
{
*(sumres + i) = '.';
j = i + 1;
}
*(sumres + j) = (char)(*(sum + i) + '0');
}
*(sumres + j) = '\0';
// 释放
free(a);
free(b);
free(sum);
return sumres;
}
// 减法算法
char *wgx_sub(char *s1, char *s2)
{
int abc = 1;
// 用于判断结果正负 负数abc赋值为-1
int s1len = strlen(s1);
int s2len = strlen(s2);
int len1 = 0, len2 = 0;
// 对拷s1和s2并转换成数字后补齐位数后的长度,若是小数其实len1和len2的长度是一样的
int count1 = 0, count11 = 0;
// 分别是第一个数的小数部分长度 和 整数部分长度
int F1 = 0;
// 用于判断是否是小数,是小数F1=1,否则F1=0
for (int i = 0; i < s1len; i++)
{
if (*(s1 + i) == '.')
{
F1 = 1;
break;
}
}
// 若是小数则 计算小数部分长度
for (int i = s1len - 1; i >= 0 && (F1 == 1); i--)
{
if (*(s1 + i) != '.')
count1++;
else
break;
}
// 计算第一个加数的整数部分长度
count11 = (F1 == 1) ? (s1len - 1 - count1) : s1len;
// 相同方法处理第二个加数
int count2 = 0, count22 = 0;
int F2 = 0;
for (int i = 0; i < s2len; i++)
{
if (*(s2 + i) == '.')
{
F2 = 1;
break;
}
}
for (int i = s2len - 1; i >= 0 && (F2 == 1); i--)
{
if (*(s2 + i) != '.')
count2++;
else
break;
}
count22 = (F2 == 1) ? (s2len - 1 - count2) : s2len;
len1 = (count1 > count2) ? count1 : count2;
int cc = len1;
// 用于后面标记小数的位置,这几行语句顺序不能颠倒
len1 += (count11 > count22) ? count11 : count22;
len2 = len1;
// 为两个加数定义为a和b并为其动态分配内存空间
int *a = (int *)malloc(sizeof(int) * len1);
int *b = (int *)malloc(sizeof(int) * len2);
if (!a || !b)
return "error";
// 初始化a和b len1和len2其实是一样大的
for (int i = 0; i < len1; i++)
{
*(a + i) = 0;
*(b + i) = 0;
}
// 定义结果变量c 以及取得长度
int len = len1;
int *c = (int *)malloc(sizeof(int) * len);
if (!c)
return "error";
// 初始化和变量
for (int i = 0; i < len; i++)
*(c + i) = 0;
int tt1 = count1 - count2;
// 用计算补齐几个0用
for (int i = s1len - 1, j = len1 - 1; j >= 0; i--, j--)
{
if (tt1 < 0)
{
while (tt1)
{
*(a + j--) = 0;
tt1++;
}
}
if (*(s1 + i) == '.')
i--;
if (i >= 0)
*(a + j) = *(s1 + i) - '0';
else
*(a + j) = 0;
}
int tt2 = count2 - count1;
// 用计算补齐几个0用
for (int i = s2len - 1, j = len2 - 1; j >= 0; i--, j--)
{
if (tt2 < 0)
{
while (tt2)
{
*(b + j--) = 0;
tt2++;
}
}
if (*(s2 + i) == '.')
i--;
if (i >= 0)
*(b + j) = *(s2 + i) - '0';
else
*(b + j) = 0;
}
// -------------------
// 判断a与b的大小,大数作为第一个数
int a1 = 0, b1 = 0;
// 用于判断a和b哪个大大的就赋值为1
for (int i = 0; i < len1; i++)
{
int tmp = *(a + i) - *(b + i);
if (tmp > 0)
{
a1 = 1;
break;
}
else if (tmp < 0)
{
b1 = 1;
abc = -1;
break;
}
// 如果循环结束a1==0 && b1==0表示a和b相等
// 结果直接为0即可
}
// a>=b时情况
int cou = 0;
if ((a1 == 1) || (a1 == 0 && b1 == 0))
for (int i = len - 1, j = len1 - 1, k = len2 - 1; i >= 0; i--, j--, k--)
{
int vv = 0;
// 是否借位的记号 借位赋值1
int n = 0;
// 借位移动了几位
cou++;
int tmp1 = *(a + j) - *(b + k);
if (cou <= len1)
{
if (tmp1 >= 0)
{
*(c + i) = tmp1;
}
else
// 向高位(低索引处借位)
{
tmp1 += 10;
*(c + i) = tmp1;
vv = 1;
}
}
else
*(c + i) = 0;
// 处理借位
if (vv == 1)
for (int ccc = j - 1; ccc >= 0 && j > 0; ccc--)
{
if (*(a + ccc) == 0)
n++;
else
break;
}
if (vv == 1)
for (int bb = n; bb < j && bb > 0; bb--)
{
*(a + j - bb) += 9;
}
// 借位处减去1
if (vv == 1 && j >= (n + 1))
*(a + (j - n - 1)) -= 1;
// 重置
vv = 0;
n = 0;
}
// a<b情况
int cou2 = 0;
if ((b1 == 1))
for (int i = len - 1, j = len1 - 1, k = len2 - 1; i >= 0; i--, j--, k--)
{
int vv = 0;
// 是否借位的记号 借位赋值1
int n = 0;
// 借位移动了几位
cou2++;
int tmp1 = *(b + k) - *(a + j);
if (cou2 <= len2)
{
if (tmp1 >= 0)
{
*(c + i) = tmp1;
}
else
// 向高位(低索引处借位)
{
tmp1 += 10;
*(c + i) = tmp1;
vv = 1;
}
}
else
*(c + i) = 0;
// 处理借位
if (vv == 1)
for (int ccc = j - 1; ccc >= 0 && j > 0; ccc--)
{
if (*(b + ccc) == 0)
n++;
else
break;
}
if (vv == 1)
for (int bb = n; bb < k && bb > 0; bb--)
{
*(b + k - bb) += 9;
}
// 借位处减去1
if (vv == 1 && k >= (n + 1))
*(b + (k - n - 1)) -= 1;
// 重置
vv = 0;
n = 0;
}
char *resc = (char *)malloc(sizeof(char) * (len + 1));
if (!resc)
return "error!";
memset(resc, '0', len + 1);
// 输出
int i, j;
for (i = 0, j = 0; i < len; i++, j++)
{
if (abc == -1)
{
*(resc + j++) = '-';
abc = 1;
}
if ((F1 || F2) && (i == (len - cc)))
{
*(resc + j++) = '.';
}
*(resc + j) = (char)(*(c + i) + '0');
}
*(resc + j) = '\0';
// 释放
free(a);
free(b);
free(c);
return resc;
}
// 大数乘法算法
char *wgx_mcl(char *s1, char *s2)
{
int F1 = 0, F2 = 0;
// 分别用于判断s1,s2是否小数,是小数对应赋值1
int n1 = 0, n2 = 0;
// 分别是源s1,s2小数位数
char *P1 = NULL, *P2 = NULL;
// 分别是源s1,s2的小数点位置索引,整数则为NULL
int len1 = strlen(s1), len2 = strlen(s2);
// 分别是源s1,s2的长度
int *product1 = (int *)malloc(sizeof(int) * (len1 + len2 + 1));
// 乘积存储区1
char *product2 = (char *)malloc(sizeof(char) * (len1 + len2 + 2));
// 乘积存储区2
memset(product1, 0, len1 + len2 + 1);
memset(product2, '0', len1 + len2 + 2);
for (int i = 0; i <= len1 + len2; i++)
{
*(product1 + i) = 0;
}
P1 = strchr(s1, '.');
P2 = strchr(s2, '.');
if (P1)
{
F1 = 1;
n1 = len1 - (int)(P1 - s1) - 1;
}
if (P2)
{
F2 = 1;
n2 = len2 - (int)(P2 - s2) - 1;
}
// 先忽略小数点 k控制递增梯度,z控制行
int tmp = 0;
for (int i = len2 - 1, z = len2 + len1; i >= 0; i--, z--)
{
// 第二个乘数遇到小数点跳过当前轮循环
if (*(s2 + i) == '.')
{
z++;
continue;
}
for (int j = len1 - 1, k = z; j >= 0; j--, k--)
{
// 第一个乘数遇到小数点跳过当前轮循环
if (*(s1 + j) == '.')
j--;
tmp = (int)(*(s1 + j) - '0') * (int)(*(s2 + i) - '0');
*(product1 + k) += tmp % 10;
// 当前位
*(product1 + k - 1) += tmp / 10;
// 进位
}
tmp = 0;
}
// 进位
for (int i = len1 + len2; i > 0; i--)
{
tmp = *(product1 + i);
*(product1 + i) = tmp % 10;
*(product1 + i - 1) += tmp / 10;
}
// product2的最高索引处放置放置'\0'
for (int i = len1 + len2, j = len1 + len2; i >= 0 && j >= 0; i--, j--)
{
if ((F1 || F2) && i == (len1 + len2 - n1 - n2))
{
*(product2 + j--) = '.';
// printf("%d",*(product2+j));
F1 = 0;
F2 = 0;
}
*(product2 + j) = (char)(*(product1 + i) + '0');
// printf("%d",*(product2+j));
}
*(product2 + len1 + len2 + 1) = '\0';
free(product1);
return product2;
}
// 大数除法算法 maxlen是计算最大精度(长度)
char *wgx_div(char *s1, char *s2, unsigned int maxlen)
{
char *s = (char *)malloc(2);
memcpy(s, "1", 2);
if (s1_compare_s2(s2, "0") == 0)
return 0;
if (s1_compare_s2(s1, s2) == 0)
return s;
if (s1_compare_s2(s1, "0") == 0 && s1_compare_s2(s2, "0") != 0)
{
memmove(s, "0", 2);
return s;
}
int len2 = strlen(s2);
int len2_end;
char *pp2 = (char *)malloc(len2 + 1);
if (!pp2)
return 0;
memcpy(pp2, s2, len2 + 1);
char *p2_poi = strchr(pp2, '.');
if (p2_poi == NULL)
return (delInvalidZeros(wgx_mcl(s1, daoshu(s2, maxlen))));
else
{
len2_end = len2 - (p2_poi - pp2) - 1;
char exarr[len2_end + 2];
memmove(p2_poi, p2_poi + 1, len2_end + 1);
exarr[0] = '1';
memset(exarr + 1, '0', len2_end);
exarr[len2_end + 1] = '\0';
return
delInvalidZeros(wgx_mcl(exarr, (delInvalidZeros(wgx_mcl(s1, daoshu(pp2, maxlen))))));
}
return 0;
}
#endif
#endif