Java的BigInteger的使用存储实现 BigDecimal的精度问题准确保留位数和四舍五入

最近在刷算法题时,遇到用 int 甚至是 long都不能表示的超大整数型数据。就只能用刚学Java时学过的java.math.BigInteger来表示。对于精度丢失问题,应该用BigDecimal来解决。

BigInteger

ps:

1.byte:byte(1字节长度)类型的整数在内存里占8位,范围是:-128(-2的7次方) ~ 127 (2的7次方-1)

字节:计算机的处理信息的最小单位。1字节=8位(bit)二进制数。

2.short:short(2个字节长度)类型整数在内存里占16位,表示范围-32,768 ~ 32,767

3.int:int(4个字节长度)类型在内存中32位,表示范围-2,147,483,648 ~ 2,147,483,647(十亿级)

4.long:long(8个字节长度)类型在内存中64位,表示范围-9,223,372,036,854,775,808

~9,223,372,036,854,775,807

正如大家所记得一样,计算机对数据的存放和运算 以 补码 形式,对数据的显示 以 原码形式;

对于 BigInteger也是一样,以二进制补码形式存放与操作。

ps:补码

最高位其实是符号位,1表示负数,0表示正数;

正数的原码/反码/补码 都一样, 负数的 补码 = 原码求反码 再 + 1;

所谓反码:对原码按位取反,只是最高符号位保持不变。

这里的原 反 补 都是以二进制形式的。

基本操作:

1.常用转型方式:

方法

说明

BigInteger(String val)

将数据字符串转化为BigInteger型数据

valueOf(long val)

返回其值等于指定 long 的值的 BigInteger。

toString()

返回此 BigInteger 的十进制字符串表示形式。

doubleValue()

将此 BigInteger 转换为 double

longValue()

将此 BigInteger 转换为 long。

2.运算方法

BigInteger 提供所有了Java 的基本整数操作符的对应物,并提供 java.lang.Math 的所有相关方法。另外,BigInteger 还提供以下运算:模算术、GCD 计算、质数测试、素数生成、位操作以及一些其他操作。 详情请看api手册

方法

说明

add(BigInteger val)

加法

multiply(BigInteger val)

乘法

divide(BigInteger val)

除法,“/“

divideAndRemainder(BigInteger val)

返回商和余数的数组

equals(Object x)

比较是否相等

abs()

绝对值

gcd(BigInteger val)

最大公约数

……

……

存储

正如前面所说,BigInteger以二进制补码形式存放.

但是 如果数据超出long的范围,那么在内存中以二进制补码的形式存放的时候,其位数会多余64,也就是会多余8个字节。

那么它到底怎么,存储的呢?

java Integer类型可以到几位数 java integer多少位_数据


由源码可见,它使用一个int型整数来存储,BigInteger的符号。

当signum为-1时,表示该数为负;当signum为0时,表示该数为0;当signum为1时,表示该数为正数。

那么,具体的值是怎么表示的呢。

java Integer类型可以到几位数 java integer多少位_java_02


由源码可得它是,通过数组来存放非符号外的其他数据信息。特别的,当BigInteger为0时,该数组的长度为0.

比如:数:12345678901234567890(已经超出long的范围)

通过debug模式可得:

java Integer类型可以到几位数 java integer多少位_数组_03


由此可见,数组中并不是存放的数据的每一位的数值

通过多组数据debug观察:

当存入的数在int类型的范围内时,数组mag[0],就会直接存放这个数。

而当超过,int而其二进制总位数不超过32时,用mag[0]一个就能表示,mag[0]的补码与真实数据

是相同的。位数也超过32就只能加上数组的mag[1]一起表示。mag[0]与mag[1]的关系为,mag[1]

后32位二进制数表示的数。mag[0]时剩余的位数表示的数,两者是相并的关系。

更大的数以此类推。

BigDecimal

在Java中进行浮点数运算的时候,会出现丢失精度的问题。这个在不需要非常准确计算是可以满足要求的。但是在某些情况下,是万万不可得。比如银行得系统中,如果出现小小得误差那么慢慢得会出现惊人得量变。而且当小数太大或需要得位太多时float和double也不能表示。

ps: Java中float的精度为6-7位有效数字。double的精度为15-16位。

对于BigDecimal得基本操作详情请看api手册。

ps:
BigDecimal除法可能出现不能整除的情况,比如 9.9/1.3,这时会报错java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.

//比较两种声明方式的精度问题
		BigDecimal a = new BigDecimal(0.001);
		BigDecimal b = new BigDecimal(0.001);
		BigDecimal c = new BigDecimal("0.001");
		BigDecimal d = new BigDecimal("0.002");
		System.out.println(a.add(a));
		System.out.println(c.add(d));

输出:
0.002000000000000000041633363423443370265886187553405761718750
0.003

由此可得当采用BigDecimal(double val)构造方法时,声明的BigDecimal类型的数据任可能出
现精度丢失问题。

故,在使用的时候,应该尽量不用该方法。可以采用将double转为String再使用BigDecimal(String val)的方法。

四舍五入

setScale(int newScale, RoundingMode roundingMode)
newScale:要返回的 BigDecimal 值的标度。
roundingMode:要应用的舍入模式。

返回 BigDecimal,其标度为指定值,其非标度值通过此 BigDecimal 的非标度值乘以或除以十的适当次幂来确定,以维护其总值。

//先将double类型转为String型,避免精度丢失,再转为BigDecimal型
		BigDecimal pi = new BigDecimal(String.valueOf(Math.PI));
		//RoundingMode:枚举类,各种舍入模式
		System.out.println(pi.setScale(7, RoundingMode.HALF_UP ));//四舍五入

舍入模式集合:

模式

解释

CEILING

向正无限大方向舍入的舍入模式。

DOWN

向零方向舍入的舍入模式。

FLOOR

向负无限大方向舍入的舍入模式。

HALF_DOWN

五舍六入

HALF_EVEN

四舍六入五作偶

HALF_UP

四舍五入

UNNECESSARY

用于断言请求的操作具有精确结果的舍入模式,因此不需要舍入。

UP

远离零方向舍入的舍入模式。

感谢观看!