高精度算法
高精度算法的设计思路和实现方法
(水段) 即便是 unsigned long long 整型也只能表示最高 264−1 的数字 (大概 18 位十进制整数). 显然不能处理对于天体物理等超大数值计算问题, 这是计算机硬件数据处理能力的限制.
我们需要占用更多字节数的整型.
我们需要利用程序设计的方式去实现这样的 高精度算法.
怎么设计
我们需要一种可根据实际数据长度改变的数字存储方式和运算方式.
为什么我们在日常生活中没有遇到这样的问题? 显然日常生活中处理较大数值运算时, 我们往往会使用列竖式的方式 (即便是口算也是如此).
计算机无法处理大数字数据根本原因在于不能一次性处理所有位, 包括日常生活中不得不使用竖式计算大数字的原因也一样, 我们不能一眼瞪出所有位的结果.
但是使用竖式计算就可以每次计算只考虑大数字的其中两位即可 (当位的计算和进位或借位), 这种方式也可以避免计算机计算时一次性处理所有位的问题.
注意到使用列竖式的计算方式具备很强的可拓展性, 只要纸足够容纳计划中最大的数字, 那么就可以进行任意长度数字的基础运算.
所以我们就可以想到使用一块由很多字节组成的内存来作为我们列竖式的 " 纸 ", 那么各个占用的字节可被统一管理的数组和字符串就是一个非常合适的工具了.
我们将需要计算的大整数的每一位都分开按顺序存储在数组的元素中.
std::string a = "1145141919810";
std::string b = "123456789";
// 一张自由伸缩的"纸"
// 上面有分别用于写两个加数的两行接下来就可以使用进位或借位的方式进行基础四则计算了.
// 加法
std::string add(std::string a, std::string b)
{
// 先声明一个函数来包装我们的功能
// 函数参数为两个存储加数的字符串, 返回值为一个存储结果的字符串
// 先预留纸上写答案的位置
std::string ans = "";
// 中间按位计算
// 最后返回答案字符串
return ans;
}接下来就该想想怎么按位计算了.
// 加法
std::string add(std::string a, std::string b)
{
// 先预留纸上写答案的位置
std::string ans = "";
// 安从低位到高位顺序逐位计算
for (
// 从距离最低位0处开始逆向遍历字符串
int i = 0;
// 直到遍历完两个字符串中较长的那个的最高位
i < std::max(a.length(), b.length());
// 每次遍历一位
i ++
)
{
// 从距离两字符串较大者最低位0处多一位的地方开始写答案
ans[std::max(a.length(), b.length())-1-i]
// 当前位的和为两个加数对应位的和
= a[a.length()-1-i] + b[b.length()-1-i]
// 同样需要考虑字符字面值问题
- '0';
}
// 返回答案
return ans;
}好吧, 出现了一些问: 万一答案比两个加数都长, 那么答案的最高位进位时就会出错;
计算较长加数比较短的加数多出来的位时, 较短加数的位数不足, 需要补 0.
而且这样的写法过于臃肿了, 像是强硬地用计算机语言来描述人类进行竖式计算的过程, 这就难以体现编程语言的优势了.
我的第一反应是倒叙运算 (即数字的最高位在字符串的最前面), 这样的好处是方便进位, 进位的时候直接往后写即可.
如果需要输出的话, 倒叙遍历答案字符串即可.
std::string add(std::string a, std::string b)
// 传入两个倒叙数字
{
std::string ans = "";
a.push_back('0'), b.push_back('0');
// 补0, 避免边界问题
char temp = '0';
// 临时存储单位的计算结果
for (int i = 0; i < std::max(a.length(), b.length()); i ++)
{
ans.push_back(
(a[std::max(a.length(), i)] + b[std::max(b.length(), i)] - '0') > '9' ?
(a[std::max(a.length(), i)] + b[std::max(b.length(), i)] - '0') % 10 + '0' :
(a[std::max(a.length(), i)] + b[std::max(b.length(), i)] - '0') + '0'
);
}
return ans;
// 返回倒叙答案
}例题讲解
工程应用
算法模板
散装函数, 先来个散装的, 能处理前导零和小数点, 能处理正负号和内存分配.(老早就想写这么个石山了😋)
char* add(char* a, char* b) // 加法
{
int sign_a = 1, sign_b = 1;
char *p_a = a;
if (*p_a == '-') { sign_a = -1; p_a++; }
else if (*p_a == '+') { sign_a = 1; p_a++; }
char *dot_a = strchr(p_a, '.');
int int_len_a = dot_a ? (int)(dot_a - p_a) : (int)strlen(p_a);
int frac_len_a = dot_a ? (int)strlen(dot_a + 1) : 0;
char *int_start_a = p_a;
char *frac_start_a = dot_a ? dot_a + 1 : NULL;
int eff_int_len_a = (int_len_a == 0) ? 1 : int_len_a;
char *p_b = b;
if (*p_b == '-') { sign_b = -1; p_b++; }
else if (*p_b == '+') { sign_b = 1; p_b++; }
char *dot_b = strchr(p_b, '.');
int int_len_b = dot_b ? (int)(dot_b - p_b) : (int)strlen(p_b);
int frac_len_b = dot_b ? (int)strlen(dot_b + 1) : 0;
char *int_start_b = p_b;
char *frac_start_b = dot_b ? dot_b + 1 : NULL;
int eff_int_len_b = (int_len_b == 0) ? 1 : int_len_b;
int max_int_len = (eff_int_len_a > eff_int_len_b) ? eff_int_len_a : eff_int_len_b;
int max_frac_len = (frac_len_a > frac_len_b) ? frac_len_a : frac_len_b;
int total_len = max_int_len + max_frac_len;
char *num1 = (char*)malloc(total_len + 1);
char *num2 = (char*)malloc(total_len + 1);
memset(num1, '0', total_len);
num1[total_len] = '\0';
memset(num2, '0', total_len);
num2[total_len] = '\0';
int offset1 = max_int_len - eff_int_len_a;
if (int_len_a > 0) {
memcpy(num1 + offset1, int_start_a, int_len_a);
}
if (frac_len_a > 0) {
memcpy(num1 + max_int_len, frac_start_a, frac_len_a);
}
int offset2 = max_int_len - eff_int_len_b;
if (int_len_b > 0) {
memcpy(num2 + offset2, int_start_b, int_len_b);
}
if (frac_len_b > 0) {
memcpy(num2 + max_int_len, frac_start_b, frac_len_b);
}
int cmp = strcmp(num1, num2);
int result_sign;
char *big, *small;
int do_add;
if (sign_a == sign_b) {
do_add = 1;
result_sign = sign_a;
big = num1;
small = num2;
} else {
if (cmp == 0) {
free(num1);
free(num2);
char *zero = (char*)malloc(2);
zero[0] = '0'; zero[1] = '\0';
return zero;
} else if (cmp > 0) {
result_sign = sign_a;
big = num1;
small = num2;
} else {
result_sign = sign_b;
big = num2;
small = num1;
}
do_add = 0;
}
char *res = (char*)malloc(total_len + 2);
res[total_len + 1] = '\0';
if (do_add) {
int carry = 0;
for (int i = total_len - 1; i >= 0; i--) {
int sum = (big[i] - '0') + (small[i] - '0') + carry;
res[i + 1] = (char)((sum % 10) + '0');
carry = sum / 10;
}
res[0] = (char)(carry + '0');
} else {
int borrow = 0;
for (int i = total_len - 1; i >= 0; i--) {
int diff = (big[i] - '0') - (small[i] - '0') - borrow;
if (diff < 0) {
diff += 10;
borrow = 1;
} else {
borrow = 0;
}
res[i + 1] = (char)(diff + '0');
}
res[0] = '0';
}
free(num1);
free(num2);
int int_start = 0;
while (int_start < max_int_len && res[int_start] == '0') {
int_start++;
}
int int_part_len = max_int_len + 1 - int_start;
int frac_start_idx = max_int_len + 1;
int frac_end = total_len;
while (frac_end >= frac_start_idx && res[frac_end] == '0') {
frac_end--;
}
int frac_part_len = frac_end - frac_start_idx + 1;
if (frac_part_len < 0) frac_part_len = 0;
int is_zero = (int_start == max_int_len && res[max_int_len] == '0' && frac_part_len == 0);
if (is_zero) {
free(res);
char *zero = (char*)malloc(2);
zero[0] = '0'; zero[1] = '\0';
return zero;
}
int out_len = 0;
if (result_sign == -1) out_len++;
out_len += int_part_len;
if (frac_part_len > 0) out_len += 1 + frac_part_len;
char *out = (char*)malloc(out_len + 1);
char *ptr = out;
if (result_sign == -1) *ptr++ = '-';
memcpy(ptr, res + int_start, int_part_len);
ptr += int_part_len;
if (frac_part_len > 0) {
*ptr++ = '.';
memcpy(ptr, res + frac_start_idx, frac_part_len);
ptr += frac_part_len;
}
*ptr = '\0';
free(res);
return out;
}
char* sub(char* a, char* b) {
char* neg_b;
if (b[0] == '-') {
neg_b = strdup(b + 1);
} else if (b[0] == '+') {
neg_b = (char*)malloc(strlen(b) + 2);
neg_b[0] = '-';
strcpy(neg_b + 1, b + 1);
} else {
neg_b = (char*)malloc(strlen(b) + 2);
neg_b[0] = '-';
strcpy(neg_b + 1, b);
}
char* res = add(a, neg_b); // 复用
free(neg_b);
return res;
}
char* mul(char* a, char* b) // 乘法
{
int sign_a = 1, sign_b = 1;
char *p_a = a;
if (*p_a == '-') { sign_a = -1; p_a++; }
else if (*p_a == '+') { sign_a = 1; p_a++; }
char *p_b = b;
if (*p_b == '-') { sign_b = -1; p_b++; }
else if (*p_b == '+') { sign_b = 1; p_b++; }
int final_sign = sign_a * sign_b;
int frac_a = 0;
int len_a = (int)strlen(p_a);
char *clean_a = (char*)malloc(len_a + 1);
int idx = 0, dot_seen = 0;
for (int i = 0; p_a[i]; i++) {
if (p_a[i] == '.') {
dot_seen = 1;
} else {
clean_a[idx++] = p_a[i];
if (dot_seen) frac_a++;
}
}
clean_a[idx] = '\0';
if (idx == 0) {
clean_a[0] = '0'; clean_a[1] = '\0'; frac_a = 0;
}
int frac_b = 0;
int len_b = (int)strlen(p_b);
char *clean_b = (char*)malloc(len_b + 1);
idx = 0; dot_seen = 0;
for (int i = 0; p_b[i]; i++) {
if (p_b[i] == '.') {
dot_seen = 1;
} else {
clean_b[idx++] = p_b[i];
if (dot_seen) frac_b++;
}
}
clean_b[idx] = '\0';
if (idx == 0) {
clean_b[0] = '0'; clean_b[1] = '\0'; frac_b = 0;
}
int total_frac = frac_a + frac_b;
int len_clean_b = (int)strlen(clean_b);
char *result_int = (char*)malloc(2);
result_int[0] = '0'; result_int[1] = '\0';
for (int i = len_clean_b - 1; i >= 0; i--) {
int digit = clean_b[i] - '0';
if (digit == 0) continue;
char *partial = (char*)malloc(2);
partial[0] = '0'; partial[1] = '\0';
for (int j = 0; j < digit; j++) {
char *temp = add(partial, clean_a);
free(partial);
partial = temp;
}
int shift = len_clean_b - 1 - i;
if (shift > 0) {
int partial_len = (int)strlen(partial);
char *shifted = (char*)malloc(partial_len + shift + 1);
strcpy(shifted, partial);
for (int k = 0; k < shift; k++)
shifted[partial_len + k] = '0';
shifted[partial_len + shift] = '\0';
free(partial);
partial = shifted;
}
char *temp = add(result_int, partial);
free(result_int);
free(partial);
result_int = temp;
}
free(clean_a);
free(clean_b);
if (strcmp(result_int, "0") == 0) {
free(result_int);
char *zero = (char*)malloc(2);
zero[0] = '0'; zero[1] = '\0';
return zero;
}
int len_res = (int)strlen(result_int);
char *final_str;
if (total_frac == 0) {
int out_len = len_res + (final_sign == -1 ? 1 : 0);
final_str = (char*)malloc(out_len + 1);
char *ptr = final_str;
if (final_sign == -1) *ptr++ = '-';
strcpy(ptr, result_int);
} else if (len_res <= total_frac) {
int out_len = 2 + total_frac;
if (final_sign == -1) out_len++;
final_str = (char*)malloc(out_len + 1);
char *ptr = final_str;
if (final_sign == -1) *ptr++ = '-';
*ptr++ = '0';
*ptr++ = '.';
int zeros = total_frac - len_res;
for (int k = 0; k < zeros; k++) *ptr++ = '0';
strcpy(ptr, result_int);
} else {
int int_part_len = len_res - total_frac;
int out_len = len_res + 1;
if (final_sign == -1) out_len++;
final_str = (char*)malloc(out_len + 1);
char *ptr = final_str;
if (final_sign == -1) *ptr++ = '-';
memcpy(ptr, result_int, int_part_len);
ptr += int_part_len;
*ptr++ = '.';
strcpy(ptr, result_int + int_part_len);
char *end = final_str + out_len - 1;
while (*end == '0') end--;
if (*end == '.') end--;
*(end + 1) = '\0';
char *check = final_str;
if (*check == '-') check++;
if (strcmp(check, "0") == 0 || strcmp(check, "0.") == 0) {
free(final_str);
final_str = (char*)malloc(2);
final_str[0] = '0'; final_str[1] = '\0';
}
}
free(result_int);
return final_str;
}
char* div(char* a, char* b, int precision) // 除法
{
if (!a || !b || precision < 0) return NULL;
int sign_a = 1, sign_b = 1;
char *p_a = a;
if (*p_a == '-') { sign_a = -1; p_a++; }
else if (*p_a == '+') { sign_a = 1; p_a++; }
char *p_b = b;
if (*p_b == '-') { sign_b = -1; p_b++; }
else if (*p_b == '+') { sign_b = 1; p_b++; }
int final_sign = sign_a * sign_b;
{
char *tmp = p_b;
int has_nonzero = 0;
while (*tmp) {
if (*tmp != '0' && *tmp != '.') { has_nonzero = 1; break; }
tmp++;
}
if (!has_nonzero) return NULL;
}
int dec_b = 0;
int b_len = (int)strlen(p_b);
char *divisor = (char*)malloc(b_len + 1);
int idx = 0, dot_seen = 0;
for (int i = 0; p_b[i]; i++) {
if (p_b[i] == '.') { dot_seen = 1; }
else {
divisor[idx++] = p_b[i];
if (dot_seen) dec_b++;
}
}
divisor[idx] = '\0';
if (idx == 0) { divisor[0] = '0'; divisor[1] = '\0'; }
{
char *s = divisor;
while (*s == '0' && *(s+1)) s++;
if (s != divisor) memmove(divisor, s, strlen(s) + 1);
}
char *int_a = NULL, *frac_a = NULL;
int int_len_a = 0, frac_len_a = 0;
char *dot_a = strchr(p_a, '.');
if (dot_a) {
int_len_a = (int)(dot_a - p_a);
frac_len_a = (int)strlen(dot_a + 1);
int_a = (char*)malloc(int_len_a + 1);
memcpy(int_a, p_a, int_len_a);
int_a[int_len_a] = '\0';
frac_a = (char*)malloc(frac_len_a + 1);
strcpy(frac_a, dot_a + 1);
} else {
int_len_a = (int)strlen(p_a);
int_a = (char*)malloc(int_len_a + 1);
strcpy(int_a, p_a);
frac_a = (char*)malloc(1);
frac_a[0] = '\0';
frac_len_a = 0;
}
int num_len = int_len_a + frac_len_a;
char *num_a = (char*)malloc(num_len + 1);
if (int_len_a > 0) memcpy(num_a, int_a, int_len_a);
if (frac_len_a > 0) memcpy(num_a + int_len_a, frac_a, frac_len_a);
num_a[num_len] = '\0';
char *dividend = NULL;
int new_dot_pos = int_len_a + dec_b;
if (new_dot_pos >= num_len) {
dividend = (char*)malloc(new_dot_pos + 1);
memcpy(dividend, num_a, num_len);
for (int i = num_len; i < new_dot_pos; i++) dividend[i] = '0';
dividend[new_dot_pos] = '\0';
} else {
int int_part_len = new_dot_pos;
int frac_part_len = num_len - new_dot_pos;
dividend = (char*)malloc(int_part_len + 1 + frac_part_len + 1);
memcpy(dividend, num_a, int_part_len);
dividend[int_part_len] = '.';
memcpy(dividend + int_part_len + 1, num_a + int_part_len, frac_part_len);
dividend[int_part_len + 1 + frac_part_len] = '\0';
}
free(num_a);
free(int_a);
free(frac_a);
char *int_part = NULL, *frac_part = NULL;
char *dot_divd = strchr(dividend, '.');
if (dot_divd) {
*dot_divd = '\0';
int_part = strdup(dividend);
frac_part = strdup(dot_divd + 1);
*dot_divd = '.';
} else {
int_part = strdup(dividend);
frac_part = strdup("");
}
{
char *s = int_part;
while (*s == '0' && *(s+1)) s++;
if (s != int_part) memmove(int_part, s, strlen(s) + 1);
}
if (strcmp(int_part, "0") == 0) {
int is_zero = 1;
for (char *p = frac_part; *p; p++) if (*p != '0') { is_zero = 0; break; }
if (is_zero) {
free(dividend); free(int_part); free(frac_part); free(divisor);
char *zero = (char*)malloc(2);
zero[0] = '0'; zero[1] = '\0';
return zero;
}
}
char *rem = strdup("0");
int max_int_quot_len = (int)strlen(int_part) + 1;
char *int_quot = (char*)malloc(max_int_quot_len);
int_quot[0] = '\0';
int int_quot_len = 0;
for (int i = 0; int_part[i]; i++) {
char digit = int_part[i];
char *rem10 = (char*)malloc(strlen(rem) + 2);
strcpy(rem10, rem);
strcat(rem10, "0");
char digit_str[2] = {digit, '\0'};
char *cur = add(rem10, digit_str);
free(rem10);
int q = 0;
while (1) {
int len_cur = (int)strlen(cur), len_div = (int)strlen(divisor);
int cmp;
if (len_cur != len_div) cmp = len_cur > len_div ? 1 : -1;
else cmp = strcmp(cur, divisor);
if (cmp < 0) break;
char *tmp = sub(cur, divisor);
free(cur);
cur = tmp;
q++;
}
int_quot[int_quot_len++] = (char)(q + '0');
int_quot[int_quot_len] = '\0';
free(rem);
rem = cur;
}
if (int_quot_len == 0) {
int_quot[0] = '0'; int_quot[1] = '\0'; int_quot_len = 1;
}
int extra = (precision >= 0) ? 1 : 0;
int target_frac_len = precision + extra;
char *frac_quot = (char*)malloc(target_frac_len + 2);
frac_quot[0] = '\0';
int frac_quot_len = 0;
int idx_frac = 0;
int frac_len = (int)strlen(frac_part);
while (frac_quot_len < target_frac_len) {
char digit;
if (idx_frac < frac_len) {
digit = frac_part[idx_frac++];
} else {
if (strcmp(rem, "0") == 0) break;
digit = '0';
}
char *rem10 = (char*)malloc(strlen(rem) + 2);
strcpy(rem10, rem);
strcat(rem10, "0");
char digit_str[2] = {digit, '\0'};
char *cur = add(rem10, digit_str);
free(rem10);
int q = 0;
while (1) {
int len_cur = (int)strlen(cur), len_div = (int)strlen(divisor);
int cmp;
if (len_cur != len_div) cmp = len_cur > len_div ? 1 : -1;
else cmp = strcmp(cur, divisor);
if (cmp < 0) break;
char *tmp = sub(cur, divisor);
free(cur);
cur = tmp;
q++;
}
frac_quot[frac_quot_len++] = (char)(q + '0');
frac_quot[frac_quot_len] = '\0';
free(rem);
rem = cur;
if (strcmp(rem, "0") == 0 && idx_frac >= frac_len) break;
}
if (frac_quot_len > precision) {
int round_up = 0;
if (frac_quot_len >= precision + 1 && frac_quot[precision] >= '5') {
round_up = 1;
}
frac_quot[precision] = '\0';
frac_quot_len = precision;
if (round_up) {
for (int i = frac_quot_len - 1; i >= 0; i--) {
if (frac_quot[i] < '9') {
frac_quot[i]++;
round_up = 0;
break;
} else {
frac_quot[i] = '0';
}
}
if (round_up) {
char *carry = (char*)malloc(3);
carry[0] = '1'; carry[1] = '\0';
char *new_int = add(int_quot, carry);
free(int_quot);
int_quot = new_int;
free(carry);
}
}
}
while (frac_quot_len > 0 && frac_quot[frac_quot_len - 1] == '0') {
frac_quot[--frac_quot_len] = '\0';
}
free(rem);
free(int_part);
free(frac_part);
free(dividend);
free(divisor);
char *trimmed_int = int_quot;
while (*trimmed_int == '0' && *(trimmed_int+1)) trimmed_int++;
int is_zero_result = (strcmp(trimmed_int, "0") == 0 && frac_quot_len == 0);
if (is_zero_result) {
free(int_quot);
free(frac_quot);
char *zero = (char*)malloc(2);
zero[0] = '0'; zero[1] = '\0';
return zero;
}
int out_len = (final_sign == -1 ? 1 : 0) + (int)strlen(trimmed_int);
if (frac_quot_len > 0) out_len += 1 + frac_quot_len;
char *out = (char*)malloc(out_len + 1);
char *ptr = out;
if (final_sign == -1) *ptr++ = '-';
strcpy(ptr, trimmed_int);
ptr += strlen(trimmed_int);
if (frac_quot_len > 0) {
*ptr++ = '.';
memcpy(ptr, frac_quot, frac_quot_len);
ptr += frac_quot_len;
}
*ptr = '\0';
free(int_quot);
free(frac_quot);
return out;
}
char* mod(char* a, char* b) // 取余
{
if (!a || !b) return NULL;
int sign_a = 1, sign_b = 1;
char *p_a = a;
if (*p_a == '-') { sign_a = -1; p_a++; }
else if (*p_a == '+') p_a++;
char *p_b = b;
if (*p_b == '-') { sign_b = -1; p_b++; }
else if (*p_b == '+') p_b++;
int frac_a = 0, frac_b = 0;
char *clean_a = (char*)malloc(strlen(p_a) + 1);
int idx_a = 0, dot_a = 0;
for (int i = 0; p_a[i]; i++) {
if (p_a[i] == '.') dot_a = 1;
else {
clean_a[idx_a++] = p_a[i];
if (dot_a) frac_a++;
}
}
clean_a[idx_a] = '\0';
char *clean_b = (char*)malloc(strlen(p_b) + 1);
int idx_b = 0, dot_b = 0;
for (int i = 0; p_b[i]; i++) {
if (p_b[i] == '.') dot_b = 1;
else {
clean_b[idx_b++] = p_b[i];
if (dot_b) frac_b++;
}
}
clean_b[idx_b] = '\0';
int max_frac = (frac_a > frac_b) ? frac_a : frac_b;
int len_a_int = idx_a + (max_frac - frac_a);
int len_b_int = idx_b + (max_frac - frac_b);
char *int_a = (char*)malloc(len_a_int + 1);
strcpy(int_a, clean_a);
for (int i = idx_a; i < len_a_int; i++) int_a[i] = '0';
int_a[len_a_int] = '\0';
char *int_b = (char*)malloc(len_b_int + 1);
strcpy(int_b, clean_b);
for (int i = idx_b; i < len_b_int; i++) int_b[i] = '0';
int_b[len_b_int] = '\0';
free(clean_a);
free(clean_b);
char *a_int = int_a;
while (*a_int == '0' && *(a_int+1)) a_int++;
char *b_int = int_b;
while (*b_int == '0' && *(b_int+1)) b_int++;
if (strcmp(b_int, "0") == 0) {
free(int_a);
free(int_b);
return NULL;
}
char *rem = strdup("0");
int a_len = (int)strlen(a_int);
for (int i = 0; i < a_len; i++) {
int rem_len = (int)strlen(rem);
char *cur = (char*)malloc(rem_len + 2);
strcpy(cur, rem);
cur[rem_len] = a_int[i];
cur[rem_len + 1] = '\0';
char *cur_stripped = cur;
while (*cur_stripped == '0' && *(cur_stripped+1)) cur_stripped++;
if (cur_stripped != cur) {
memmove(cur, cur_stripped, strlen(cur_stripped) + 1);
}
while (1) {
int cmp = 0;
int cur_len = (int)strlen(cur);
int b_len = (int)strlen(b_int);
if (cur_len > b_len) cmp = 1;
else if (cur_len < b_len) cmp = -1;
else cmp = strcmp(cur, b_int);
if (cmp < 0) break;
char *tmp = sub(cur, b_int);
free(cur);
cur = tmp;
}
free(rem);
rem = cur;
}
free(int_a);
free(int_b);
char *rem_stripped = rem;
while (*rem_stripped == '0' && *(rem_stripped+1)) rem_stripped++;
if (rem_stripped != rem) {
memmove(rem, rem_stripped, strlen(rem_stripped) + 1);
}
if (strcmp(rem, "0") == 0) {
free(rem);
char *zero = (char*)malloc(2);
zero[0] = '0'; zero[1] = '\0';
return zero;
}
char *result;
if (sign_a < 0) {
result = (char*)malloc(strlen(rem) + 2);
result[0] = '-';
strcpy(result + 1, rem);
} else {
result = strdup(rem);
}
free(rem);
return result;
}
char* pow(char* a, char* b, int precision) // 指数
{
if (!a || !b) return NULL;
if (precision < 0) precision = 10;
int sign_a = 1;
const char *p_a = a;
if (*p_a == '-') { sign_a = -1; p_a++; }
else if (*p_a == '+') p_a++;
int sign_b = 1;
const char *p_b = b;
if (*p_b == '-') { sign_b = -1; p_b++; }
else if (*p_b == '+') p_b++;
int b_int_len = 0, b_frac_len = 0;
const char *dot = strchr(p_b, '.');
if (dot) {
b_int_len = (int)(dot - p_b);
b_frac_len = (int)strlen(dot + 1);
} else {
b_int_len = (int)strlen(p_b);
}
char *abs_b = (char*)malloc(b_int_len + b_frac_len + 2);
int idx = 0;
for (int i = 0; i < b_int_len; i++) abs_b[idx++] = p_b[i];
if (b_frac_len > 0) {
abs_b[idx++] = '.';
for (int i = 0; i < b_frac_len; i++) abs_b[idx++] = dot[i+1];
}
abs_b[idx] = '\0';
int b_is_integer = 1;
if (b_frac_len > 0) {
for (int i = 0; i < b_frac_len; i++)
if (dot[i+1] != '0') { b_is_integer = 0; break; }
}
int base_is_zero = 1;
for (const char *s = p_a; *s; s++) {
if (*s != '0' && *s != '.') { base_is_zero = 0; break; }
}
if (base_is_zero) {
if (strcmp(abs_b, "0") == 0 || (b_is_integer && strcmp(abs_b, "0") == 0)) {
free(abs_b);
return NULL;
}
if (sign_b < 0) {
free(abs_b);
return NULL;
}
free(abs_b);
char *zero = (char*)malloc(2);
zero[0] = '0'; zero[1] = '\0';
return zero;
}
if (strcmp(abs_b, "0") == 0) {
free(abs_b);
char *one = (char*)malloc(2);
one[0] = '1'; one[1] = '\0';
return one;
}
if (b_is_integer) {
char *exp_str = strdup(p_b);
if (dot) exp_str[b_int_len] = '\0';
char *strip = exp_str;
while (*strip == '0' && *(strip+1)) strip++;
if (strip != exp_str) memmove(exp_str, strip, strlen(strip)+1);
char str_two[] = "2";
char *parity = mod(exp_str, str_two);
int exp_odd = (strcmp(parity, "1") == 0);
free(parity);
char *base = strdup(p_a);
char *exp_copy = strdup(exp_str);
char *result = strdup("1");
while (strcmp(exp_copy, "0") != 0) {
char *mod2 = mod(exp_copy, str_two);
int odd = (strcmp(mod2, "1") == 0);
free(mod2);
if (odd) {
char *tmp = mul(result, base);
free(result);
result = tmp;
}
char *tmp_base = mul(base, base);
free(base);
base = tmp_base;
char *new_exp;
if (strcmp(exp_copy, "0") == 0) {
new_exp = strdup("0");
} else {
int len = strlen(exp_copy);
char *half = (char*)malloc(len + 1);
int carry = 0, idx_h = 0;
for (int i = 0; i < len; i++) {
int d = exp_copy[i] - '0' + carry * 10;
int q = d / 2;
carry = d % 2;
if (q > 0 || idx_h > 0) half[idx_h++] = q + '0';
}
if (idx_h == 0) half[idx_h++] = '0';
half[idx_h] = '\0';
new_exp = half;
}
free(exp_copy);
exp_copy = new_exp;
}
free(base);
free(exp_copy);
free(exp_str);
if (sign_b < 0) {
char one[] = "1";
char *inv = div(one, result, precision);
free(result);
result = inv;
}
if (sign_a < 0 && exp_odd) {
int is_zero = 1;
for (char *p = result; *p; p++)
if (*p != '0' && *p != '.') { is_zero = 0; break; }
if (!is_zero) {
char *signed_res = (char*)malloc(strlen(result) + 2);
signed_res[0] = '-';
strcpy(signed_res+1, result);
free(result);
result = signed_res;
}
}
free(abs_b);
return result;
}
if (sign_a < 0) {
free(abs_b);
return NULL;
}
auto exp_func = [&](char *x) -> char* {
char *sum = strdup("1");
char *term = strdup("1");
char *x_copy = strdup(x);
char k_buf[20];
for (int k = 1; k <= 150; k++) {
sprintf(k_buf, "%d", k);
char *tmp1 = mul(term, x_copy);
char *tmp2 = div(tmp1, k_buf, precision);
free(term); free(tmp1);
term = tmp2;
char *tmp3 = add(sum, term);
free(sum);
sum = tmp3;
int is_zero = 1;
for (char *p = term; *p; p++) {
if (*p != '0' && *p != '.' && *p != '-') { is_zero = 0; break; }
}
if (is_zero) break;
}
free(term);
free(x_copy);
return sum;
};
auto ln_func = [&](char *x) -> char* {
char *y = strdup("0");
char two[] = "2";
for (int iter = 0; iter < 12; iter++) {
char *ey = exp_func(y);
char *diff = sub(x, ey);
char *sum_xe = add(x, ey);
char *term = mul(two, diff);
char *delta = div(term, sum_xe, precision);
free(term); free(diff); free(sum_xe); free(ey);
char *new_y = add(y, delta);
free(y); free(delta);
y = new_y;
}
return y;
};
char *a_abs = strdup(p_a);
char *ln_a = ln_func(a_abs);
free(a_abs);
char *b_signed;
if (sign_b < 0) {
b_signed = (char*)malloc(strlen(abs_b) + 2);
b_signed[0] = '-';
strcpy(b_signed + 1, abs_b);
} else {
b_signed = strdup(abs_b);
}
free(abs_b);
char *power = mul(b_signed, ln_a);
free(b_signed);
free(ln_a);
char *result = exp_func(power);
free(power);
if (result[0] == '-') {
int is_zero = 1;
for (int i = 1; result[i]; i++)
if (result[i] != '0' && result[i] != '.') { is_zero = 0; break; }
if (is_zero) {
free(result);
result = strdup("0");
}
}
return result;
}
char* sqrt(char* a, char* b, int precision) // 开方
{
if (!a || !b || precision < 0) return NULL;
int sign_a = 1;
const char *p_a = a;
if (*p_a == '-') { sign_a = -1; p_a++; }
else if (*p_a == '+') p_a++;
int sign_b = 1;
const char *p_b = b;
if (*p_b == '-') { sign_b = -1; p_b++; }
else if (*p_b == '+') p_b++;
const char *dot_b = strchr(p_b, '.');
int len_b = dot_b ? (int)(dot_b - p_b) : (int)strlen(p_b);
char *exp_str = (char*)malloc(len_b + 1);
memcpy(exp_str, p_b, len_b);
exp_str[len_b] = '\0';
if (dot_b) {
for (const char *s = dot_b + 1; *s; s++)
if (*s != '0') {
free(exp_str);
return NULL;
}
}
char *strip = exp_str;
while (*strip == '0' && *(strip + 1)) strip++;
if (strip != exp_str) memmove(exp_str, strip, strlen(strip) + 1);
if (strcmp(exp_str, "0") == 0) {
free(exp_str);
return NULL;
}
int a_is_zero = 1;
for (const char *s = p_a; *s; s++)
if (*s != '0' && *s != '.') { a_is_zero = 0; break; }
if (a_is_zero) {
free(exp_str);
char *zero = (char*)malloc(2);
zero[0] = '0'; zero[1] = '\0';
return zero;
}
if (sign_a < 0) {
char two_str[] = "2";
char *rem = mod(exp_str, two_str);
int even = (strcmp(rem, "0") == 0);
free(rem);
if (even) {
free(exp_str);
return NULL;
}
}
int guard = 10;
int work_prec = precision + guard;
char *a_abs = strdup(p_a);
char *n_str = strdup(exp_str);
char *n_minus_one = sub(n_str, (char*)"1");
int need_reciprocal = (sign_b < 0);
char *x = strdup(a_abs);
if (strcmp(x, "0") == 0) {
free(x); free(a_abs); free(n_str); free(n_minus_one); free(exp_str);
char *zero = (char*)malloc(2);
zero[0] = '0'; zero[1] = '\0';
return zero;
}
for (int iter = 0; iter < 200; iter++) {
char *x_pow = strdup("1");
char *exp_copy = strdup(n_minus_one);
char *base_pow = strdup(x);
while (strcmp(exp_copy, "0") != 0) {
char *mod2 = mod(exp_copy, (char*)"2");
int odd = (strcmp(mod2, "1") == 0);
free(mod2);
if (odd) {
char *tmp = mul(x_pow, base_pow);
free(x_pow);
x_pow = tmp;
}
char *tmp_base = mul(base_pow, base_pow);
free(base_pow);
base_pow = tmp_base;
int len = strlen(exp_copy);
char *half = (char*)malloc(len + 1);
int carry = 0, idx_h = 0;
for (int i = 0; i < len; i++) {
int d = exp_copy[i] - '0' + carry * 10;
int q = d / 2;
carry = d % 2;
if (q > 0 || idx_h > 0) half[idx_h++] = q + '0';
}
if (idx_h == 0) half[idx_h++] = '0';
half[idx_h] = '\0';
free(exp_copy);
exp_copy = half;
}
free(exp_copy);
free(base_pow);
char *term1 = mul(n_minus_one, x);
char *term2 = div(a_abs, x_pow, work_prec);
free(x_pow);
char *numerator = add(term1, term2);
free(term1); free(term2);
char *new_x = div(numerator, n_str, work_prec);
free(numerator);
char *diff = sub(new_x, x);
char *diff_abs = (diff[0] == '-') ? diff + 1 : diff;
int th_len = 2 + (work_prec - 1);
char *threshold = (char*)malloc(th_len + 1);
threshold[0] = '0'; threshold[1] = '.';
for (int i = 0; i < work_prec - 1; i++) threshold[2 + i] = '0';
threshold[th_len - 1] = '1';
threshold[th_len] = '\0';
char *check = sub(threshold, diff_abs);
int converged = (check[0] != '-');
free(check);
free(threshold);
free(diff);
if (converged) {
free(x);
x = new_x;
break;
}
free(x);
x = new_x;
}
char *pow10 = strdup("1");
for (int i = 0; i < precision; i++) {
char *tmp = mul(pow10, (char*)"10");
free(pow10);
pow10 = tmp;
}
char *scaled = mul(x, pow10);
char *half_str = strdup("0.5");
char *scaled_plus = add(scaled, half_str);
free(scaled); free(half_str);
char *rounded_scaled;
{
char *dot = strchr(scaled_plus, '.');
if (dot) *dot = '\0';
rounded_scaled = strdup(scaled_plus);
if (dot) *dot = '.';
}
free(scaled_plus);
char *result = div(rounded_scaled, pow10, precision);
free(rounded_scaled);
free(pow10);
if (sign_a < 0) {
char *neg_result = (char*)malloc(strlen(result) + 2);
neg_result[0] = '-';
strcpy(neg_result + 1, result);
free(result);
result = neg_result;
}
if (need_reciprocal) {
char *one = strdup("1");
char *inv = div(one, result, precision);
free(one);
free(result);
result = inv;
}
free(x);
free(a_abs);
free(n_str);
free(n_minus_one);
free(exp_str);
return result;
}
char* log(char* a, char* b, int precision) // 对数
{
if (!a || !b || precision < 0) return NULL;
int sign_a = 1;
const char *p_a = a;
if (*p_a == '-') { sign_a = -1; p_a++; }
else if (*p_a == '+') p_a++;
int a_is_zero = 1;
for (const char *s = p_a; *s; s++)
if (*s != '0' && *s != '.') { a_is_zero = 0; break; }
if (a_is_zero || sign_a < 0) return NULL;
{
char *one = strdup("1");
char *a_copy = strdup(p_a);
int alen = strlen(a_copy);
while (alen > 1 && a_copy[alen-1] == '0') { a_copy[--alen] = '\0'; }
if (alen > 1 && a_copy[alen-1] == '.') a_copy[--alen] = '\0';
if (strcmp(a_copy, "1") == 0) {
free(one); free(a_copy);
return NULL;
}
free(one); free(a_copy);
}
int sign_b = 1;
const char *p_b = b;
if (*p_b == '-') { sign_b = -1; p_b++; }
else if (*p_b == '+') p_b++;
int b_is_zero = 1;
for (const char *s = p_b; *s; s++)
if (*s != '0' && *s != '.') { b_is_zero = 0; break; }
if (b_is_zero || sign_b < 0) return NULL;
{
char *b_copy = strdup(p_b);
int blen = strlen(b_copy);
while (blen > 1 && b_copy[blen-1] == '0') { b_copy[--blen] = '\0'; }
if (blen > 1 && b_copy[blen-1] == '.') b_copy[--blen] = '\0';
if (strcmp(b_copy, "1") == 0) {
free(b_copy);
char *zero = (char*)malloc(2);
zero[0] = '0'; zero[1] = '\0';
return zero;
}
free(b_copy);
}
auto exp_func = [&](char *x) -> char* {
char *sum = strdup("1");
char *term = strdup("1");
char *x_copy = strdup(x);
char k_buf[20];
int max_iters = 150;
for (int k = 1; k <= max_iters; k++) {
sprintf(k_buf, "%d", k);
char *tmp1 = mul(term, x_copy);
char *tmp2 = div(tmp1, k_buf, precision + 10);
free(term); free(tmp1);
term = tmp2;
char *tmp3 = add(sum, term);
free(sum);
sum = tmp3;
int is_zero = 1;
for (char *p = term; *p; p++) {
if (*p != '0' && *p != '.' && *p != '-') { is_zero = 0; break; }
}
if (is_zero) break;
}
free(term);
free(x_copy);
return sum;
};
auto ln_func = [&](char *x) -> char* {
char *y = strdup("0");
char two[] = "2";
int max_iters = 12;
for (int iter = 0; iter < max_iters; iter++) {
char *ey = exp_func(y);
char *diff = sub(x, ey);
char *sum_xe = add(x, ey);
char *term = mul(two, diff);
char *delta = div(term, sum_xe, precision + 10);
free(term); free(diff); free(sum_xe); free(ey);
char *new_y = add(y, delta);
free(y); free(delta);
y = new_y;
}
return y;
};
char *ln_b = ln_func(strdup(p_b));
char *ln_a = ln_func(strdup(p_a));
char *result = div(ln_b, ln_a, precision);
free(ln_b);
free(ln_a);
return result;
}
封装成类, 直接当正常浮点数用, 能直接赋值和计算.(这个也老早就想写了😋)
class HighPrecision
{
private:
public:
};
这篇文档还在撰写中
内容正在加紧编写和反复打磨。你可以先去上一篇,也可以悄悄掀开围挡,看看现在写到哪了……草稿状态下的文章内可能含各种各样的错误内容,仅供满足好奇心,勿作为正式参考。