大数的四则运算
历经10天时间完成了整数巨大数的四则运算,一方面由于太懒没有同时完成小数巨大数部分,另一方面小数巨大书的完成也是建立在整数的基础之上,这也是没有同时完成小数部分的一个主要原因。完成整数是一个摸索的过程,完成之后在技术上有一定的成熟,再转向小数效率会更加的高,整体大框架也会更好,不必大作改动。
实现大数的目的在于,解决超出计算机可表示范围数据的存储以及运算,计算机中各类型数据可表示的范围见头文件limist.h,这里摘取部分代码,以int为例当超过-217483648~2147483647范围数字计算机就不能正常表示,需要设计一种数据结构来存储大数,大数在密码学中广泛运用,大数的运算往往涉及上百千位的运算。本篇文章将重点讲述大数的存储以及大数的四则运算。
#define MB_LEN_MAX 2 /* max. # bytes in multibyte char */
#define SHRT_MIN (-32768) /* minimum (signed) short value */
#define SHRT_MAX 32767 /* maximum (signed) short value */
#define USHRT_MAX 0xffff /* maximum unsigned short value */
#define INT_MIN (-2147483647 - 1) /* minimum (signed) int value */
#define INT_MAX 2147483647 /* maximum (signed) int value */
#define UINT_MAX 0xffffffff /* maximum unsigned int value */
#define LONG_MIN (-2147483647L - 1) /* minimum (signed) long value */
#define LONG_MAX 2147483647L /* maximum (signed) long value */
#define ULONG_MAX 0xffffffffUL /* maximum unsigned long value */
#if _INTEGRAL_MAX_BITS >= 8
#define _I8_MIN (-127i8 - 1) /* minimum signed 8 bit value */
#define _I8_MAX 127i8 /* maximum signed 8 bit value */
#define _UI8_MAX 0xffui8 /* maximum unsigned 8 bit value */
#endif
#if _INTEGRAL_MAX_BITS >= 16
#define _I16_MIN (-32767i16 - 1) /* minimum signed 16 bit value */
#define _I16_MAX 32767i16 /* maximum signed 16 bit value */
#define _UI16_MAX 0xffffui16 /* maximum unsigned 16 bit value */
#endif
#if _INTEGRAL_MAX_BITS >= 32
#define _I32_MIN (-2147483647i32 - 1) /* minimum signed 32 bit value */
#define _I32_MAX 2147483647i32 /* maximum signed 32 bit value */
#define _UI32_MAX 0xffffffffui32 /* maximum unsigned 32 bit value */
#endif
1、大数的存储
大数获取采取从txt文件中获得,将得到的数据按照万进制存储到int的数据,这里解释一下我们采取的为什么是万进制而不是更大的进制,因为不仅要完成巨大数的存储还要完成四则运算,在涉及乘法的部分,10000*10000小于整型的上限2147483647,不会产生溢出的情况,而更大的进制10万相乘则会溢出,同时从文件中读取的数据为十进制数字,而10000是10的次方,在转换进制的方面万进制提供了很好的便利,只需要每4位取出一个十进制数字存放到数组中即可,所以说10000是最合适的进制。
那么根据上述分析可以得到大数的结构体如下,number_sign来表示大数的正负,0正1负,number_array表示巨大数的数据部分,number_count记录数组的长度。
typedef struct HUGENUMBER {
char number_sign; //标记正负符号
int *number_array; //指向存储巨大数的数组
int number_count; //数组中元素个数
}HUGENUMBER;
读取文件的时候我们先得到文件中数据的长度file_length,假设文件提供的数据都是合法字符,不存在字母的情况,这里需要注意的是文件中可能存放数据第一个字符可能是’-’或者’+’,巨大数的实际长度就需要相应变化,如果读到的第一个字符是0-9之间说明没有正负号字符,反之则存在正负号字符需要对上面得到的file_length减一。根据得到的文件长度就可以去申请存放巨大数数组空间,数组的长度number_count = (file_length + 3) / 4,存放的时候先存放巨大数最高位的数据,因为最高位的数字不一定是4个,例如234 5268 5982,先存放高位的234,存放最高位数字技巧如下:
在把巨大数从文件中存放到结构体数组的函数里,给出了一个只读数组,作用在与存放最大数的最高位数字,通过file_length % 4,当做数组下标取出字符串,当做fscanf函数的格式符便可取出最高位的数字。
const char *format[4] = {"%4d", "%1d","%2d", "%3d"};
fscanf(fp,format[file_length % 4], &myNumber->number_array[myNumber->number_count- 1]);
剩下数字都可以每4位取得。存放的方式遵循将巨大数高位存放在数组高位,低位存放在低位,为后续的加减提供便利,存储的部分就完成。如下图
存储数据部分代码如下
//The read file operation in this section is only for integer operations,
// not counting the decimal, and needs to be modified.
void initHugeNumber(HUGENUMBER *myNumber, FILE *fp) {
int file_length;
int beginIndex;
int i;
char firstSign;
const char *format[4] = {"%4d", "%1d", "%2d", "%3d"};
fseek(fp, 0, SEEK_END);
file_length = ftell(fp);
fseek(fp, 0, SEEK_SET);
fread(&firstSign, sizeof(char), 1, fp);
//先读一个字符判断是不是数字,决定巨大数实际起始位置
beginIndex = ('0' <= firstSign && firstSign <= '9') ? 0 : 1;
//文件中巨大数实际实际起始位置
file_length += ('0' <= firstSign && firstSign <= '9') ? 0 : -1;
//文件中巨大数实际长度 以此来计算申请数组长度
fseek(fp, beginIndex, SEEK_SET);
myNumber->number_sign = (firstSign == '-') ? NEGATIVE_SIGN : POSTIVE_SIGN;
myNumber->number_count = (file_length + 3) / 4;
myNumb