您的位置:首页 > 编程语言 > Java开发

JAVA实现高精度整数加减乘

2020-03-06 16:42 1091 查看

首先,需要指出的是,java内的BigInteger类很好用,功能相当全面。像加减乘这样的基础操作都是可以胜任的。所以,在java中其实没有手动实现高精度的必要。

所以写下这篇博客的初衷并不是放在实际情况下使用,而是当做高精度算法的练习,也同时练习java编程能力。

封装类内容:

实现接口

克隆接口Cloneable,重写clone方法。便于复制

可比较接口Comparable,实现CompareTo方法,便于比较排序。
(可搭配各种数据结构使用)

成员变量

数组number,用于记录每一位,需要指出的是,该类仅支持十进制的运算。

数组长度len,用于表示数组中数字长度,便于统计和计算

符号变量sign,用于作为符号项参与运算。取值为1或-1.

成员方法:

构造方法

提供两种构造方法,用于初始化

传入整数,按位倒序转化进数组

传入字符串,检索符号,倒序存入数组

全局方法

打印方法void print,以带符号字符串的形式打印。

高精度加法void add,传入大整数,加到对象上

高精度减法void minus,传入大整数,计算减法

高精度乘法void multiply,传入大整数,计算乘法

克隆方法BigNumber clone,深克隆对象,返回克隆对象

比较方法int compareTo,与传入大整数进行比较,大1,小-1,相等0

私有方法

整理数组void sort,传入计算后的长度,计算符号并整理数组

代码

public class BigNumber implements Cloneable,Comparable<BigNumber>
{
short[] number = new short[50000];
int len = 0;
int sign;
BigNumber(int num)
{
if(num < 0)
{
num = -num;
this.sign = -1;
}
else
{
this.sign = 1;
}
while(num != 0)
{
this.number[++len] = (short)(num % 10);
num /= 10;
}
}

BigNumber(String num)
{
this.len = num.length();
this.sign = 1;
for(int i = this.len - 1;i > 0;i--)
{
number[this.len - i] = (short)(num.charAt(i) - '0');
}
if(num.charAt(0) == '-')
{
this.len -= 1;
this.sign = -1;
}
else
{
this.number[this.len] = (short)(num.charAt(0) - '0');
}
}

public BigNumber clone()
{
BigNumber num = null;
try {
num = (BigNumber) super.clone();
} catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
num.number = number.clone();
return num;
}

public void print()
{
if(sign == -1)
{
System.out.print("-");
}
for(int i = len;i >= 1;i--)
{
System.out.print(this.number[i]);
}
System.out.println();
}

private void sort(int len)
{
while(this.number[len + 1] != 0)
{
len++;
}

if(this.number[len] < 0)
{
this.sign = -this.sign;
for(int i = 1;i < len;i++)
{
if(this.number[i] == 0)
{
continue;
}
this.number[i] = (short)(10 - this.number[i]);
this.number[i + 1]++;
}
this.number[len] *= -1;
}
while(len > 1 &&this.number[len] == 0)
{
len--;
}
this.len = len;
}

public void add(BigNumber num)
{
int len = Math.max(this.len, num.len);
for(int i = 1;i <= len ;i++)
{
this.number[i] = (short)((this.number[i] * this.sign) + (num.number[i] * num.sign));
this.number[i + 1] += this.number[i] / 10;
this.number[i] %= 10;
if(this.number[i] < 0)
{
this.number[i + 1] --;
this.number[i] += 10;
}
}
sort(len);
}

public void minus(BigNumber num)
{
int len = Math.max(this.len, num.len);
for(int i = 1;i <= len ;i++)
{
this.number[i] = (short)((this.number[i] * this.sign) - (num.number[i] * num.sign));
this.number[i + 1] += this.number[i] / 10;
this.number[i] %= 10;
if(this.number[i] < 0)
{
this.number[i + 1] --;
this.number[i] += 10;
}
}
sort(len);
}

public void multiply(BigNumber num)
{
BigNumber ans = new BigNumber(0);
for(int i = 1;i <= this.len;i++)
{
for(int j = 1;j <= num.len;j++)
{
ans.number[i + j - 1] += (short)(this.number[i] * num.number[j] * this.sign * num.sign * ans.sign);
ans.number[i + j] += ans.number[i + j - 1] / 10;
ans.number[i + j - 1] %= 10;
if(ans.number[i + j - 1] < 0)
{
ans.number[i + j]--;
ans.number[i + j - 1] += 10;
}
}
ans.sort(i + num.len);
}
number = ans.number.clone();
sign = ans.sign;
len = ans.len;
}
@Override
public int compareTo(BigNumber o) {
o.minus(this);
if(o.len == 0)
{
return 0;
}
return o.sign == 1 ? -1 : 1;
}
}

主要算法实现细节:

加法&减法

首先,为了处理负数的相加减。允许数组在计算时出现负数,并且边计算边整理的。

基本思路是尾对齐并按位直接相加减。由于在数组中数据逆序存放,所以尾对齐是默认设置。所以计算时仅需要从第一位开始至两者长度中较大者循环即可。

对每一位,取每一位的绝对值乘上该数符号作为真实值进行加减。算完后,进行整理,主要是进位和借位,保证每一位的值落在0-9上。

对于首位,允许出现负数,当首位为负数是,表示该数符号与当前符号相反,交于整理方法进行整理,传递数组长度为两者长度较长者。

整理方法

对计算完成的数进行整理,具体分为三步

第一步,从当前位开始找到最高位。方法就是从向后寻找第一个0.(当然这样的处理当当前位与最高位之间存在零的时候会出现bug,但其作为内部方法,通过在调用前对长度的准确把握,还是可以避开这个bug的。)

第二步,根据最高位整理数组并更新符号。 主要针对的,是当数组中的值为负的情况。由于在计算处理时,采用的是边算边进位。若出现负数,最终一定会出现一个负的最高位,其余位为正。当检测到最高位为负数时,翻转符号,最高位取相反数,从最低位至次高位开始翻转符号,并借位归正。例如 -1 2 9 符号为正, 整理后变为 7 1 ,符号为负。

第三步,从最高位起,计算真实长度。由于负数情况可能导致位数减少,该操作即为解决这种情况。实现方法为从最高位起,找到首个不为零的位。并处理答案为0的情况。并修正长度和符号成员变量。

乘法

乘法的高精度不能直接在原数组上进行,故定义答案变量ans。

模拟乘法竖式,每一次,取以为与另一因数相乘,取该位下标为偏移量,统计在答案数组中,并进行整理。

需要注意的是,在向答案数组中累加时,需要额外乘上答案变量的符号以确保符号统一。

  • 点赞
  • 收藏
  • 分享
  • 文章举报
wayne_lee_lwc 发布了14 篇原创文章 · 获赞 2 · 访问量 326 私信 关注
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: