C++高精度浮点类HPFloat 通过数量级运算

高三一模完了,打算趁着寒假好好折腾一下C++。前几天看了一个高精度浮点类WFloat,很有意思

它是通过整数和小数分别处理的方式实现的浮点,但可以注意到,C++中浮点(double、float)是通过数量级进行处理,精度有限但大小是几乎不限。如果仿照它们写一个浮点类,会非常有趣。

数据的存储可以用数组或向量,数组当然会快一些,但考虑到数组实际上有上限,就使用向量,更灵活。至于数量级,锁定记录首位的数量级就好。

先是头文件的引用:

#pragma once
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;

再把类体给写出来:

class HPFloat
{
private:
    long long Digit;					  //数量级
    vector Num;			  //数位
    bool Symbol;						  //符号
    void empty();						  //清空
    long long Sdig() const;				  //有效长度
    void Simp();						  //去零标准化
    void Bspace();						  //整体退位
    void Mfor(long long x);				  //前移
    void Mback(long long x);			  //后移
    long long Mindig() const;			  //最小数量级
    void Lplus(char op, long long psi);	  //坐标加法
    void Dplus(char op, long long d);	  //数位加法
    void Lsubcor(char op, long long psi); //坐标减法核心
    void Lsub(char op, long long psi);	  //坐标减法
    void Dsub(char op, long long d);	  //数位减法
public:
    static bool outtype;
    static const HPFloat hpfzero;
    HPFloat();
    template 
    HPFloat(Ti value);
    HPFloat(const HPFloat &value);
    void editlen(long long l);			//定义长度
    long long Len() const;				  //全长
    string expstr();
    double expflo();
    HPFloat &operator=(const HPFloat &num);
    HPFloat &operator=(const long long &op);
    HPFloat &operator=(const string &num);
    HPFloat &operator=(const double &num);
    HPFloat &operator=(const int &op);
    template 
    HPFloat &operator=(const Ty &op);
    friend HPFloat abs(const HPFloat &op1);
    friend HPFloat operator+(const HPFloat &op1, const HPFloat &op2);
    friend HPFloat operator-(const HPFloat &op1, const HPFloat &op2);
    friend HPFloat operator*(const HPFloat &op1, const HPFloat &op2);
    friend HPFloat operator/(const HPFloat &op1, const HPFloat &op2);
    friend HPFloat operator-(const HPFloat &op);
    friend bool operator==(const HPFloat &op1, const HPFloat &op2);
    friend bool operator!=(const HPFloat &op1, const HPFloat &op2);
    friend bool operator<(const HPFloat &op1, const HPFloat &op2);
    friend bool operator<=(const HPFloat &op1, const HPFloat &op2);
    friend bool operator>(const HPFloat &op1, const HPFloat &op2);
    friend bool operator>=(const HPFloat &op1, const HPFloat &op2);
    friend HPFloat operator+=(HPFloat &op1, const HPFloat &op2);
    friend HPFloat operator-=(HPFloat &op1, const HPFloat &op2);
    friend HPFloat operator*=(HPFloat &, const HPFloat &);
    friend HPFloat operator/=(HPFloat &, const HPFloat &);
    friend ostream &operator<<(ostream &out, const HPFloat &op);
    friend istream &operator>>(istream &in, HPFloat &op);
};

const HPFloat hpfzero(0);
bool HPFloat::outtype=0;

顺便定义一个0,便于后面处理。

然后,功能一一实现。从构造函数出发:

c++在类型转换的时候会调用到构造函数,所以为了兼容其它类型数据,构造函数要能通过参数设定数值。赋值可以交给后面的等号重载实现,所以构造函数的设计不难。可以直接函数模板伺候。

HPFloat::HPFloat()
{
    for (int i = 0; i < 16; i++)
    {
        Num.push_back(0);
    }
    Symbol = 0;
    Digit = 0;
    return;
}

template < typename Ti>
HPFloat::HPFloat(Ti value)
{
    for (long long i = 0; i < 16; i++)
    {
        Num.push_back(0);
    }
    *this=value;
    return;
}

考虑到复制构造函数需要带着精度一起传递,单独写出来

HPFloat::HPFloat(const HPFloat& value)
{
    vector ().swap(Num);//作为复制构造函数时先清空
    for (long long i = 0; i < value.Len(); i++)//按参数的精度构造
    {
        Num.push_back(value.Num[i]);
    }
    Digit=value.Digit;
    Symbol=value.Symbol;
    *this=value;
    return;
}

然后要写定义精度的editlen,这玩意也比较容易,不涉及数量级。它要分两种情况考虑,一是扩容,二是收缩。扩容没啥可说的,直接push_back,但收缩时还得考虑下四舍五入,这姑且交给后面的Mback函数,通过反复推拉两次,解决问题。

void HPFloat::editlen(long long l)
{
    if (l < 8)//精度过低时直接返回
    {
        return;
    }
    if (l > Len())//判断需要扩容还是收缩
    {
        long long olen = Len();
        for (long long i = 0; i < l - olen; i++)
        {
            Num.push_back(0);
        }
    }
    else
    {
        Mfor(Len() - Sdig() - 1);
        Mback(Len() - l);
        Num.erase(Num.end() - Len() + l, Num.end());
    }
    Simp();
}

然后,解决工具类函数,比较简单。

long long HPFloat::Len() const
{
    return Num.size();
}

void HPFloat::empty()
{
    for (long long i = 0; i < Len(); i++)//清空数据,保留精度
    {
        Num[i] = 0;
    }
    Digit = 0;
    return;
}

long long HPFloat::Sdig() const
{
    if (Num[Len() - 1] != 0)//如果精度全部占满,返回全长
    {
        return (Len() - 1);
    }
    for (long long i = Len() - 1; i >= 0; i--)//计算开头零的数量
    {
        if (Num[i] == 0)
        {
            continue;
        }
        else
        {
            return i;
        }
    }
    return -1;//全空时返回-1
}

void HPFloat::Simp()
{
    long long i = 0;
    while (Num[i] == 0)//计算末尾的零
    {
        i++;
    }
    if (i == 0)//无需化简,返回
    {
        return;
    }
    if (i == Len())//如果全空
    {
        Digit = 0;
        Symbol = 0;//数量级和符号归零
        return;
    }
    else
    {
        Mback(i);//后退去零
    }
}

void HPFloat::Bspace()
{
    char a = Num[0];//记录末位
    long long sd=Sdig();
    for (long long i = 1; i <= sd; i++)
    {
        Num[i - 1] = Num[i];
        Num[i] = 0;
    }
    if (a >= 5)//四舍五入
    {
        Lplus(1, 0);
    }
    return;
}

void HPFloat::Mfor(long long x)
{
    if (x == 0)
    {
        return;
    }
    if (x < 0)//负值则后移
    {
        Mback(-x);
        return;
    }
    long long sd=Sdig();
    for (long long i = sd; i >= 0; i--)
    {
        Num[x + i] = Num[i];
        Num[i] = 0;
    }
    return;
}

void HPFloat::Mback(long long x)
{
    if (x == 0)
    {
        return;
    }
    if (x < 0)
    {
        Mfor(-x);
        return;
    }
    char t = 0;
    long long l = Sdig();
    if (x > l + 1)//移动大于精度,全清,保留数量级
    {
        for (long long i = 0; i <= l; i++)
        {
            Num[i] = 0;
        }
        return;
    }
    if (x == l + 1)
    {
        t = Num[l];
    }
    for (long long i = x; i <= l; i++)
    {
        Num[i - x] = Num[i];
        Num[i] = 0;
    }
    for (long long i = l - x + 1; i < Len(); i++)//清理未移动的数位
    {
        Num[i] = 0;
    }
    if (t >= 5)
    {
        Lplus(1, 0);//四舍五入后进位
    }
    return;
}

long long HPFloat::Mindig() const
{
    return Digit - Sdig();
}

然后,噩梦开始,整个……先不急着搞运算,从比较开始。

大于、小于比较,按符号→数量级→首位开始各数位的顺序比就好

>=、<=、==就更没啥了

bool operator==(const HPFloat &op1, const HPFloat &op2)
{//先判断符号、数量级、已用精度
    if (!(op1.Digit == op2.Digit && op1.Sdig() == op2.Sdig() && op1.Symbol == op2.Symbol))
    {
        return 0;
    }
    long long ago=op1.Sdig();//很久以前,C++是多么艺术❛‿˂̵✧
    for (long long i = 0; i <= ago; i++)
    {
        if (op1.Num[i] != op2.Num[i])//数位一一比较
        {
            return 0;
        }
    }
    return 1;//全部通过,输出1
}

bool operator!=(const HPFloat &op1, const HPFloat &op2)
{
    return !(op1 == op2);
}

bool operator<(const HPFloat &op1, const HPFloat &op2)
{//按符号、数量级、已用精度、各数位的顺序比较
    if (op1 == hpfzero && op2 != hpfzero)//判断零
    {
        return !op2.Symbol;
    }
    if (op2 == hpfzero)//零的数量级比较特殊,不参与比较
    {
        return op1.Symbol;
    }
    if (op1.Symbol != op2.Symbol)//比较符号
    {
        return op1.Symbol;
    }
    if (op1.Digit != op2.Digit)//比较数量级
    {
        return (op1.Digit < op2.Digit);
    }
    long long sd1=op1.Sdig();
    long long sd2=op2.Sdig();
    long long t = (sd1 < sd2) ? sd1 : sd2;//提前准备数据,提升效率
    for (long long i = 0; i <= t; i++)
    {
        if (op1.Num[sd1 - i] != op2.Num[sd2 - i])
        {
            return (op1.Num[sd1 - i] < op2.Num[sd2 - i]);//比较各数位
        }
    }
    if (sd1 < sd2)//比较已用精度
    {
        return 1;
    }
    return 0;
}

bool operator<=(const HPFloat &op1, const HPFloat &op2)
{
    return (op1 < op2 || op1 == op2);
}

bool operator>(const HPFloat &op1, const HPFloat &op2)
{//和<基本一致
    if (op1 == hpfzero)
    {
        return op2.Symbol;
    }
    if (op2 == hpfzero && op1 != hpfzero)
    {
        return !op1.Symbol;
    }
    if (op1.Symbol != op2.Symbol)
    {
        return op2.Symbol;
    }
    if (op1.Digit != op2.Digit)
    {
        return (op1.Digit > op2.Digit);
    }
    long long sd1=op1.Sdig();
    long long sd2=op2.Sdig();
    long long t = (sd1 < sd2) ? sd1 : sd2;
    for (long long i = 0; i <= t; i++)
    {
        if (op1.Num[sd1- i] != op2.Num[sd2 - i])
        {
            return (op1.Num[sd1 - i] > op2.Num[sd2 - i]);
        }
    }
    if (sd1 > sd2)
    {
        return 1;
    }
    return 0;
}

bool operator>=(const HPFloat &op1, const HPFloat &op2)
{
    return (op1 > op2 || op1 == op2);
}

然后是负号和绝对值

HPFloat abs(const HPFloat &op)
{
	HPFloat ans(op);
	ans.Symbol = 0;
	return ans;
}

HPFloat operator-(const HPFloat &op)
{
	HPFloat ans(op);
	ans.Symbol = !op.Symbol;
	return ans;
}

再往后,运算开始

加法优先,我的思路是劈开处理,从整个数,劈开成个位数,然后把数量级换算成坐标。于是,先写坐标运算。输入一个要加的数值和位置,递归进位。顶到头就退一位精度,首位进位时把数量级也进一位。

void HPFloat::Lplus(char op, long long psi)
{
	if (psi > Sdig())//如果在已用精度之前,直接填充
	{
		Digit += (psi - Sdig());
		Num[psi] = op;
		Simp();
		return;
	}
	Num[psi] += op;
	if (Num[psi] >= 10)//是否进位
	{
		if (psi == (Len() - 1))//如果精度已满
		{
			Bspace();//退格让出一位精度
			Digit++;
			Num[psi] = 1;
		}
		else
		{
			Lplus(1, psi + 1);
		}
	}
	for (long long i = 0; i < Len(); i++)//处理进位剩下的数据。
	{//不在进位时处理为防止出现重复运算
		if (Num[i] >= 10)
		{
			Num[i] -= 10;
		}
	}
	Simp();
	return;
}

然后是数位加法,把数量级换算到坐标。先判断精度够不够,如果精度够不着,就直接return,能够着就整体平移使末位与之对齐。

void HPFloat::Dplus(char op, long long d)
{
	Simp();
	if (Digit >= d)    //判断谁的数量级大
	{
		if (Digit - (Len() - 1) - 1 > d)//够不着就直接return
		{
			return;
		}
		if (Digit - (Len() - 1) - 1 == d)//差一位就四舍五入掉
		{
			if (op >= 5)
			{
				Mfor(Len() - Sdig() - 1);
				Lplus(1, 0);
				return;
			}
			else
			{
				return;
			}
		}
		if (Mindig() <= d)//是否需要平移来对齐
		{
			Lplus(op, Sdig() - (Digit - d));
		}
		else
		{
			Mfor(Mindig() - d);
			Num[0] = op;
		}
	}
	else
	{
		if (Mindig() + Len() - 1 >= d)//是否需要平移
		{
			Num[d - Digit + Sdig()] = op;//精度足够,直接填充
			Digit = d;
		}
		else
		{
			Mback(d - (Mindig() + Len() - 1));//精度不足,把自己舍掉
			Num[Len() - 1] = op;
			Digit = d;
		}
	}
	Simp();
	return;
}

然后加等,直接把数位一个个拆开传入即可。加法就调用加等。

HPFloat operator+=(HPFloat &op1, const HPFloat &op2)
{
	if (op1.Symbol != op2.Symbol)//如果符号不同,传递给减法
	{
		HPFloat op3(op2);
		if (op1.Symbol == 1)
		{
			op1.Symbol = 0;
			op3 -= op1;
			op1 = op3;
			return op1;
		}
		else
		{
			op3.Symbol = 0;
			op1 -= op3;
			return op1;
		}
	}
	{
		if (op1 == hpfzero)//如果为零,直接返回
		{
			op1 = op2;
			return op1;
		}
	}
	long long sd=op2.Sdig();
	long long mi=op2.Mindig();
	for (long long i = 0; i <= sd; i++)//一个个传入到Dplus
	{
		op1.Dplus(op2.Num[i], mi + i);
	}
	HPFloat ans(op1);
	return ans;
}

HPFloat operator+(const HPFloat &op1, const HPFloat &op2)
{
	HPFloat op3(op1);
	op3 += op2;
	return op3;
}

减法,实在是难伺候。如果在首位以外减时每一位都要循环退位,过于复杂,所以先姑且禁止小减大。因为减法的数量级可能”突变“,例如1001-1000,数量级从3直接跳到1。因为减法不会增加数量级不用考虑进位,所以我把减法的递归运算部分和数量级运算部分分开,单个数位计算完后统一处理数量级。其他部分和加法类似,从坐标运算开始。

void HPFloat::Lsubcor(char op, long long psi)
{
	Num[psi] -= op;
	if (Num[psi] < 0)
	{
		Lsubcor(1, psi + 1);//递归借位
	}
	for (long long i = 0; i < Len(); i++)//处理借位剩下的负值
	{
		if (Num[i] < 0)
		{
			Num[i] += 10;
		}
	}
}

void HPFloat::Lsub(char op, long long psi)//递归处理后再计算数量级
{
	long long ld = Sdig();
	Lsubcor(op, psi);
	Digit -= ld - Sdig();
	Simp();
	return;
}

数位运算上和加法基本一致。

void HPFloat::Dsub(char op, long long d)
{
	if (Digit - (Len() - 1) - 1 > d)    //够不着就直接return
	{
		return;
	}
	if (Digit - (Len() - 1) - 1 == d)//差一位就四舍五入掉
	{
		if (op >= 5)
		{
			Mfor(Len() - Sdig() - 1);
			Lsub(1, 0);
			return;
		}
		else
		{
			return;
		}
	}
	if (Mindig() <= d)//是否需要平移来对齐
	{
		Lsub(op, Sdig() - (Digit - d));
	}
	else
	{
		Mfor(Mindig() - d);
		Lsub(op, 0);
	}
	Simp();
	return;
}

减法就通过循环传递参数到Dsub

HPFloat operator-=(HPFloat &op1, const HPFloat &op2)
{
	HPFloat ans(op1);
	ans = op1 - op2;
	op1 = ans;
	return ans;
}

HPFloat operator-(const HPFloat &op1, const HPFloat &op2)
{
	if (op1.Symbol != op2.Symbol)//符号不同就传递给加法
	{
		if (op1.Symbol == 1)
		{
			HPFloat ans(op1);
			ans.Symbol = 0;
			ans = op1 + op2;
			ans.Symbol = 1;
			return ans;
		}
		else
		{
			HPFloat ans(op2);
			ans.Symbol = 0;
			ans += op1;
			return ans;
		}
	}
	if (op1 == hpfzero)
	{
		return -op2;
	}
	bool bg=(op1 >= op2);//确保传递时不发生小减大
	HPFloat ans(bg ? op1 : op2);
	HPFloat op((!bg)?op1 : op2);
	ans.Symbol = (!bg);
	ans.Simp();
	long long D = op.Mindig();
	long long sd=op.Sdig();
	for (long long i = 0; i <= sd; i++)//循环传递参数
	{
		ans.Dsub(op.Num[i], D + i);
	}
	return ans;
}

加减两个基础运算伺候完了,剩下乘除只要基于它们进行就好。

乘法,就让每两位相乘然后带着数量级相加即可

HPFloat operator*(const HPFloat &op1, const HPFloat &op2)
{
	HPFloat ans;
	ans.editlen(op1.Len());
	long long sd1=op1.Sdig();
	long long sd2=op2.Sdig();
	long long mi1=op1.Mindig();
	long long mi2=op2.Mindig();//提前提取需要重复使用的数据,提升效率
	for (long long i = 0; i <= sd1; i++)
	{
		for (long long j = 0; j <= sd2; j++)
		{
			char t = op1.Num[i] * op2.Num[j];
			if (t==0)
			{
				continue;//零不参与运算
			}
			HPFloat temp;
			temp = t;
			temp.Digit = mi1 + mi2 + i + j + (t >= 10);//计算两位相乘的数量级
			ans += temp;//直接加进去
		}
	}
	ans.Symbol = (op1.Symbol != op2.Symbol);//同号为正,异号为负
	return ans;
}

HPFloat operator*=(HPFloat &op1, const HPFloat &op2)
{
	op1 = op1 * op2;
	HPFloat ans(op1);
	return ans;
}

除法,模仿竖式运算,通过减来实现,减到精度用尽为止。多一位精度为了四舍五入。

HPFloat operator/(const HPFloat &op1, const HPFloat &op2)
{
	if (op2 == hpfzero)//定义除零错误
	{
		throw("ZeroDivide");
	}
	HPFloat ans;
	ans.editlen(op1.Len()+1);
	HPFloat num1(op1);
	HPFloat num2(op2);
	ans.Symbol = !(op1.Symbol == op2.Symbol);//确定符号
	ans.Digit = op1.Digit - op2.Digit;//确定数量级
	num1.Digit = 0;
	num2.Digit = 0;
	num1.Symbol = 0;
	num2.Symbol = 0;//全部对齐,便于运算
	ans.Digit -= (num1 < num2);//确定数量级

	for (long long i = ans.Len() - 1; i >= 0; i--)//遍历ans输入商
	{
		while (num1 >= num2)//得出商
		{
			ans.Num[i]++;
			num1 -= num2;
		}
		if (num1 == hpfzero || num2 == hpfzero)//除尽时退出
		{
			break;
		}
		num2.Digit--;
	}
	ans.Mback(1);//退位,四舍五入掉最后一位
	ans.Simp();
	HPFloat temp(ans);
	return temp;
}

HPFloat operator/=(HPFloat &op1, const HPFloat &op2)
{
    op1 = op1 / op2;
    HPFloat ans(op1);
    return ans;
}

之前一直用着的等号重载,先是自己跟自己赋值,要考虑四舍五入:

HPFloat &HPFloat::operator=(const HPFloat &op)
{
    HPFloat num;//定义一个临时对象,避免遇到如a=a的语句时出现问题
    num.editlen(op.Len());
    long long sd=op.Sdig();
    for (long long i = 0; i <=sd; i++)
    {
        num.Num[i] = op.Num[i];
    }
    num.Digit = op.Digit;
    num.Symbol = op.Symbol;//需要手动负值,因为构造函数会调用=
    empty();
    long long i = (Len() - 1 > sd) ? sd : (Len() - 1);
    for (long long j = 0; j <= i; j++)
    {
        Num[Len() - 1 - j] = num.Num[sd - j];
    }
    if (!((Len() - 1) >= sd))
    {
        if (num.Num[sd - (Len() - 1) - 1] >= 5)//精度不足时四舍五入
        {
            Lplus(1, 0);
        }
    }
    Symbol = num.Symbol;
    Digit = num.Digit;
    Simp();
    return *this;
}

然后,当然要支持和整型的赋值。鉴于long long最长且其他整型都可以转换成它,就用它解决整型。把每一位循环读出存入就好:

HPFloat &HPFloat::operator=(const long long &op)
{
    empty();
    long long num = (op > 0) ? op : -op;//取正
    short e = 0;
    {
        long long t = 1;
        while (t <= num)
        {
            e++;//计算数位
            t *= 10;
        }
    }
    for (short i = 0; i <= e; i++)
    {
        Num[i] = num % 10;//逐个填入
        num = (num - num % 10) / 10;
    }
    Digit = e - 1;
    Symbol = (op < 0);
    Simp();
    return *this;
}

int类型比较常用,转换成long long存入:

HPFloat &HPFloat::operator=(const int &op)//int较常用,不参与函数模板提升效率
{//longlong可兼容int,调用longlong
    return *this = (long long)op;
}

然后,要支持和最万能的string转换。这一步最复杂,需要先切掉符号,结束符;要支持科学计数法;还要考虑到用户可能输入一些不太规范的内容,如开头多出很多0、小数点加在了开头或结尾……什么?你还要输一些汉字和字母?我直接给你throw算了。

那就先扫描有没有e或E,有就先录入数量级,然后把e之后的切掉,继续提取各数位。遇到小数点就再修改一遍数量级,继续录入;发现在48到57之外的字符,就throw

HPFloat &HPFloat::operator=(const string &num)
{
    HPFloat temp;
    temp.editlen(num.size());
    unsigned long eloc = 0;
    while (num[eloc] != 'e' && num[eloc] != 'E' && eloc <= (num.size() - 1))//如果是科学计数法,先提出数量级
    {
        eloc++;
    }
    if (eloc < num.size() - 1)
    {
        temp.Digit = stoll(num.substr(eloc + 1, num.size() - 1));//写入数量级
    }
    string op = num.substr(0, eloc );
    {
        if (op[0] == '-')//提出负号
        {
            temp.Symbol = 1;
            op.erase(0, 1);
        }
        {
            long long l = 0;
            while (op[l] == '0')//删掉多余的零
            {
                l++;
            }
            if (l != 0)
            {
                op.erase(0, l);
            }
        }
        reverse(op.begin(), op.end());//倒序,和HPFloat的顺序相同
        if (op[0] == '\0')//删掉结束符
        {
            op.erase(0, 1);
        }
        bool ifdot = 0;
        long long dotloc = 0;
        for (unsigned long long i = 0; i < op.size(); i++)
        {
            if (op[i] == '.')//记录小数点的位置
            {
                ifdot = 1;
                dotloc = i;
                continue;
            }
            if (op[i] < 48 || op[i] > 57)//遇到异常数据,抛出错误
            {
                throw("DataException");
                return *this;
            }
            temp.Num[i - ifdot] = op[i] - 48;//录入数据
        }
        if (op[op.size() - 1] == '.')//如果小数点在末尾
        {
            op.erase(op.size() - 1, 1);
            long long l = 0;
            while (op[op.size() - 1 - l] == '0')
            {
                l++;
            }
            temp.Digit += -l - 1;//算出数量级
        }
        else
        {
            temp.Digit += op.size() - 1 - dotloc - ifdot;//算出数量级
        }
        temp.Simp();//化简返回
        *this = temp;
        return *this;
    }
}

浮点数就可以先转成字符再录入

HPFloat &HPFloat::operator=(const double& num)
{
    char tem[16]= {0};
    sprintf(tem,"%f",num);//打印到字符串
    string str=tem;
    *this=str;//调用等号重载
    return *this;
}

其它类型,转成浮点伺候

template < typename Ty>
HPFloat &HPFloat::operator=(const Ty &op)
{
    return *this = (double)op;//转换到浮点
}

然后,搞一下输出,写两个类型转换函数。转到浮点比较简单,循环加就行;转到字符稍复杂,不过比录入简单一些

string HPFloat::expstr()
{
    string str;
    if (!outtype)//判断输出类型
    {
        if (Symbol==1)//正负
        {
            str+='-';
        }
        if (Digit < 0)//是否需要补零
        {
            str+= "0.";
            for (long long i = -1; i > Digit; i--)
            {
                str+= '0';
            }
        }
        for (long long i = Sdig(); i >= 0; i--)
        {
            str+= (char)((int)Num[i]+48);//循环输出
            if (Sdig() - i == Digit && i != 0)
            {
                str+= '.';//小数点
            }
        }
        for (long long i = Mindig(); i > 0; i--)//结尾补零
        {
            str+= '0';
        }
    }
    else
    {
        if (Symbol==1)
        {
            str+='-';
        }
        str+= (char)((int)Num[Sdig()]+48);
        str+= '.';//把首位和.先输出
        for (long long i = Sdig() - 1; i >= 0; i--)
        {
            str+= (char)((int)Num[i]+48);//循环输出
        }
        str+= 'e' << Digit;//输出数量级
    }
    return str;
}

double HPFloat::expflo()
{
    double ans=0;
    double temp=0;
    for (long long i = 0; i <= Sdig(); i++)
    {
        temp=Num[i];
        temp*=pow(10,Mindig()+i);
        ans+=temp;//循环加法输出
    }
    return ans;
}

输入输出,输出直接照抄expstr即可,输入嘛,就拿string做个介质

考虑到string理论上有上限,所以输出不调用expstr

istream &operator>>(istream &in, HPFloat &op)
{
    string temp;
    in >> temp;//输入到字符,调用等号重载
    op = temp;
    return in;
}

ostream &operator<<(ostream &out, const HPFloat &op)
{//和expstr基本相同。因为string理论上有上限,所以这里不调用expstr
    if (!op.outtype)
    {
        if (op.Symbol==1)
        {
            out<<'-';
        }
        if (op.Digit < 0)
        {
            out << "0.";
            for (long long i = -1; i > op.Digit; i--)
            {
                out << '0';
            }
        }
        for (long long i = op.Sdig(); i >= 0; i--)
        {
            out << (int)op.Num[i];
            if (op.Sdig() - i == op.Digit && i != 0)
            {
                out << '.';
            }
        }
        for (long long i = op.Mindig(); i > 0; i--)
        {
            out << '0';
        }
    }
    else
    {
        if (op.Symbol==1)
        {
            out<<'-';
        }
        out << (int)op.Num[op.Sdig()] << '.';
        for (long long i = op.Sdig() - 1; i >= 0; i--)
        {
            out << (int)op.Num[i];
        }
        out << 'e' << op.Digit;
    }
    return out;
}

到此,整个类大概就完事了,测试、、、测试,简直不敢运行,一出毛病就得查几个小时

//测试函数
#include 
#include 
#include "HPFloat.h"
using namespace std;
int main()
{
	HPFloat a;
	a.editlen(600);
	HPFloat b(a);
	cin >> a >> b;
	cout << "---------------------------"  << endl;
	cout << "a:" << a << "\nb:" << b << endl;
	cout << endl;
	cout << "a>b?:"<<(a > b);
	cout << endl;
	cout << "a+b:"<<(a + b);
	cout << endl;
	cout << "a-b:"<< (a - b);
	cout << endl;
	HPFloat::outtype = 1;
	cout << "a*b:"<< (a * b);
	cout << endl;
	cout << "a/b:"<< (a / b);
	getch();
	return 0;
}

C++高精度浮点类HPFloat 通过数量级运算_第1张图片

 除法计算稍慢一点,毕竟300位……这谁知道算的对不对呢,换了几个小点的数,计算器摁一摁,大概是没问题

完整的HPFloat.h可以见我的Gitee:

HPFloat.h · GlowingBrick/Do As One Pleases - Gitee.com

你可能感兴趣的:(c++,开发语言,后端)