在银行系统及天文运算中,可能存在超出精度的情况。于是,借助特殊的数据存储方式进行大实数运算显得尤为重要。多数开源社区给出的代码为大整数的四则运算,对于含有小数点的实数不适用,于是特此编写大实数的加法与乘法。
鉴于笔者能力有限,程序使用数组与长度标记组成顺序表、按位存储大实数。实数的整数部分与小数部分分别存储,各提供25位的存储精度,实数整体提供50位存储精度。笔者的问题背景不涉及减法与除法,所以没有对上述功能予以实现。
//存储结构定义
#pragma once
#include <iostream>
#include <algorithm>
#include <string>
constexpr auto PRECISION = 25; //最大精度(小数与整数保留同样长度)
using namespace std;
class Tax
{
private:
bool isNeg; //符号标记(非正数)
short integer[PRECISION]; //大整数
short decimal[PRECISION]; //小数
short ADP_Prec; //小数精度
short INT_Prec; //整数精度
public:
Tax(); //无参构造函数
Tax(string str); //有参构造函数
Tax operator+(Tax B); //加法重载
Tax operator*(Tax B); //乘法重载
void display() const; //小数显示
};
//存储结构实现
#include "TaxRate.hpp"
Tax::Tax()
{
isNeg = 0;
ADP_Prec = 0;
INT_Prec = 0;
for (int i = 0; i < PRECISION; i++)
{
decimal[i] = 0;
integer[i] = 0;
}
}
Tax::Tax(string str)
{
isNeg = 0;
ADP_Prec = 0;
INT_Prec = 0;
for (int i = 0; i < PRECISION; i++)
{
decimal[i] = 0;
integer[i] = 0;
}
int in = 0;
for (in = 0; str[in] != '.'; in++)
;
for (int i = (in - 1); i >= 0; i--)
if (str[i] >= '0' && str[i] <= '9')
{
integer[in - i - 1] = short(str[i] - '0');
INT_Prec++;
}
for (int j = (INT_Prec + 1); j < str.length(); j++)
if (str[j] >= '0' && str[j] <= '9')
{
decimal[j - INT_Prec - 1] = short(str[j] - '0');
ADP_Prec++;
}
}
Tax Tax::operator+(Tax B)
{
short extra = 0; //进位标记
Tax result; //结果
result.ADP_Prec = ((this->ADP_Prec > B.ADP_Prec) ? this->ADP_Prec : B.ADP_Prec); //结果小数位
for (int i = result.ADP_Prec - 1; i >= 0; i--)
{
short temp = this->decimal[i] + B.decimal[i] + extra;
result.decimal[i] = temp % 10;
extra = temp / 10;
}
int j = 0;
while (j < this->INT_Prec && j < B.INT_Prec)
{
short temp = this->integer[j] + B.integer[j] + extra;
result.integer[j] = temp % 10;
extra = temp / 10;
j++;
}
for (; j < this->INT_Prec; j++)
{
short temp = this->integer[j] + extra;
result.integer[j] = temp % 10;
extra = temp / 10;
}
for (; j < B.INT_Prec; j++)
{
short temp = B.integer[j] + extra;
result.integer[j] = temp % 10;
extra = temp / 10;
}
result.INT_Prec = ((this->INT_Prec > B.INT_Prec) ? this->INT_Prec : B.INT_Prec) + extra;
if (extra)
result.integer[result.INT_Prec - 1] = 1;
return result;
}
Tax Tax::operator*(Tax B)
{
short ANoDot[2 * PRECISION] = {0}; //被乘数(去掉小数点)
short BNoDot[2 * PRECISION] = {0}; //乘数(去掉小数点)
short resultNoDot[4 * PRECISION] = {0}; //结果(去掉小数点)
for (int i = 0; i < PRECISION; i++)
{
ANoDot[i] = this->integer[PRECISION - 1 - i]; //被乘数第0~24位对应整数部分第24~0位
ANoDot[PRECISION + i] = this->decimal[i]; //被乘数第25~59位对应小数部分第0~24位
BNoDot[i] = B.integer[PRECISION - 1 - i]; //乘数第0~24位对应整数部分第24~0位
BNoDot[PRECISION + i] = B.decimal[i]; //乘数第25~59位对应小数部分第0~24位
}
//笛卡尔乘法
for (int i = 0; i < (this->ADP_Prec + this->INT_Prec); i++)
{
for (int j = 0; j < (B.ADP_Prec + B.INT_Prec); j++)
{
resultNoDot[2 * PRECISION + this->ADP_Prec + B.ADP_Prec - i - j - 1] += ANoDot[PRECISION + this->ADP_Prec - i - 1] * BNoDot[PRECISION + B.ADP_Prec - j - 1];
}
}
//进位清算
short extra = 0;
for (int i = (4 * PRECISION - 1); i >= 0; i--)
{
short temp = resultNoDot[i] + extra;
resultNoDot[i] = temp % 10;
extra = temp / 10;
}
//结果转存
Tax result;
for (int i = 0; i < PRECISION; i++)
{
result.decimal[i] = resultNoDot[2 * PRECISION + i];
result.integer[i] = resultNoDot[2 * PRECISION - i - 1];
}
result.INT_Prec = this->INT_Prec + B.INT_Prec;
if (!result.integer[result.INT_Prec - 1])
result.INT_Prec--;
result.ADP_Prec = this->ADP_Prec + B.ADP_Prec;
return result;
}
void Tax::display() const
{
for (int i = (INT_Prec - 1); i >= 0; i--)
cout << integer[i];
cout << ".";
for (int j = 0; j < ADP_Prec; j++)
cout << decimal[j];
cout << endl;
}
由于加法与乘法的实现都采用了运算符重载,以上算法依旧存在精度溢出的可能(2个乘数的位数长度分别为a和b,则相加结果的位数至少是max{a, b}、相乘结果的位数至少是a + b – 1)。在实际使用时,可以通过修改宏PRECISION的值来满足运算精度。
因为只是算法样例,笔者不提供主函数代码,有需要的朋友可以自行编写主函数进行实际运用。
【2018年9月23日完成 数据结构(C++)】