首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >BigDecimal,BigInteger 学习以及简单示例

BigDecimal,BigInteger 学习以及简单示例

作者头像
发布2019-08-29 11:23:19
1.2K0
发布2019-08-29 11:23:19
举报
文章被收录于专栏:WD学习记录WD学习记录

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。

本文链接:https://cloud.tencent.com/developer/article/1495111

最近的项目中用到了BigDecimal,之前并没有深入学习使用过,只是大概知道可以用于精确的运算,而float和double是不精确的。

BigDecimal的实现中用到了BigIntegr,因此这里先学习下BigInteger。

BigInteger

int和long都有长度限制,如果需要计算的整数大小超过long的范围,那么可以用到BigInteger。

BigInteger继承自抽象类Number。

// 符号,-1-负数,0-0,1-正数
final int signum;
//以大尾数顺序表示的这个大整数的大小:这个数组的第0个元素是该大小中最重要的整数。
//大小必须“最小”,因为最重要的int(@code mag[0])必须为非零。
//这对于确保每个biginteger值只有一个表示是必要的。注意,这意味着biginteger zero有一个零长度的mag数组。
// mag表示的是正数的原码字节数组。mag数组是存储BigInteger数值大小的,采用big-endian的顺序,也就是高位字节存入低地址,低位字节存入高地址.
// http://www.mamicode.com/info-detail-1360414.html
final int[] mag;
// 冗余字段
private int bitCount;
// 冗余字段
private int bitLength;
// 冗余字段
private int lowestSetBit;
// 冗余字段
private int firstNonzeroIntNum;
// 用于获取int值
final static long LONG_MASK = 0xffffffffL;
// 数组最大长度
private static final int MAX_MAG_LENGTH = Integer.MAX_VALUE / Integer.SIZE + 1; // (1 << 26)
// PRIME_SEARCH_BIT_LENGTH_LIMIT 大于这个长度的大数会导致BitSieve.singleSearch方法溢出
private static final  int PRIME_SEARCH_BIT_LENGTH_LIMIT = 500000000;
// 两个大数的mag[] 长度都大于这个值,将会使用Karatsuba multiplication
private static final int KARATSUBA_THRESHOLD = 80;
// 如果两个数mag长度都大于KARATSUBA_THRESHOLD,且至少一个的长度大于这个限度,将会使用3-way Toom-Cook multiplication
private static final int TOOM_COOK_THRESHOLD = 240;
// 如果大数的数组长度大于该限制,将会使用Karatsuba squaring
private static final int KARATSUBA_SQUARE_THRESHOLD = 128;
// 如果大数的数组长度大于该限制,将会使用Toom-Cook squaring
private static final int TOOM_COOK_SQUARE_THRESHOLD = 216;
// 下面几个参数作用类似,暂时不记录,等到以后需要时再细看
static final int BURNIKEL_ZIEGLER_THRESHOLD = 80;
static final int BURNIKEL_ZIEGLER_OFFSET = 40;
private static final int SCHOENHAGE_BASE_CONVERSION_THRESHOLD = 20;
private static final int MULTIPLY_SQUARE_THRESHOLD = 20;
private static final int MONTGOMERY_INTRINSIC_THRESHOLD = 512;

构造函数

// 该部分注释来自于 http://www.mamicode.com/info-detail-1360414.html
public BigInteger(byte[] val) {
        if (val.length == 0)
            throw new NumberFormatException("Zero length BigInteger");

        // 如果第一个字节是负数,则这个byte[] val就是负数的补码。因此通过补码的逆运算(补码的补码)可以得到负数的绝对值,再将符号位设置为-,则得到这个补码所代表的负数。
        // 如果参数字节数组以-1开头,不管几个,只要-1是连续的,那么这些-1都看成是符号-,这些-1的下一个字节才是有效字节。如果不以-1开头而是其他负数,则有效字节从索引0开始。
        if (val[0] < 0) {
            mag = makePositive(val);
            signum = -1;
        } else {
            // 如果第一个字节是整数,则采用stripLeadingZeroBytes方法,将每个字节的二进制补码按顺序连接起来后去掉开头的0后返回。
            mag = stripLeadingZeroBytes(val);
            signum = (mag.length == 0 ? 0 : 1);
        }
        if (mag.length >= MAX_MAG_LENGTH) {
            checkRange();
        }
    }
// 将一个包含大数的二进制补码的的int数组转换为biginteger
private BigInteger(int[] val) {
        if (val.length == 0)
            throw new NumberFormatException("Zero length BigInteger");

        if (val[0] < 0) {
            mag = makePositive(val);
            signum = -1;
        } else {
            mag = trustedStripLeadingZeroInts(val);
            signum = (mag.length == 0 ? 0 : 1);
        }
        if (mag.length >= MAX_MAG_LENGTH) {
            checkRange();
        }
    }
// 在上边两个构造函数中加上了signum来判断符号的正负
public BigInteger(int signum, byte[] magnitude);

private BigInteger(int signum, int[] magnitude)
// 把val按照radix进制转化为大数,val中可以包含一个可选的-或+,不可以有空格
public BigInteger(String val, int radix)
// 10进制的BigInteger(String val, int radix)方法
public BigInteger(String val)

其他的暂时看不下去了,等待需要用到的时候看吧,令人头秃。

先看几个常用的方法:

// 返回一个大整数,其值等于指定的@code long。此“静态工厂方法”优先于(@code long)构造函数提供,因为它允许重用常用的大整数。
// 如果值在-16-16之间,那么返回的BigInteger是同一个对象。
public static BigInteger valueOf(long val)

// 原因,以下代码在静态代码块中,返回时就是返回这两个数组对应位置的对象
for (int i = 1; i <= MAX_CONSTANT; i++) {
    int[] magnitude = new int[1];
    magnitude[0] = i;
    posConst[i] = new BigInteger(magnitude,  1);
    negConst[i] = new BigInteger(magnitude, -1);
}

使用BigInteger做加减乘除运算时,分别需要调用实例方法:

BigInteger test1 = BigInteger.valueOf(7);
BigInteger test2 = BigInteger.valueOf(8);
BigInteger testNeg = BigInteger.valueOf(-1);
BigInteger r1 = test1.add(test2);
BigInteger r2 = test1.subtract(test2);
BigInteger r3 = test1.multiply(test2);
BigInteger r4 = test1.divide(test2);

和long相比,BigInteger不会有长度限制,但是计算效率较低。

BigInteger也是不可变类,可以转换为基本类型,转换时会丢失高位信息。

BigDecimal

BigDecimal可以表示一个任意大小且精度完全准确的浮点数。

// 待完善
private final BigInteger intVal;
// 表示小数位数
private final int scale;  
// 小数的位数,如果小数位数未知则为0,如果非0,保证值时正确的
private transient int precision;

// 用于存储规范的字符串表示形式
private transient String stringCache;

// intCompact的值,表示有意义的部分只能从intVal中获得
static final long INFLATED = Long.MIN_VALUE;

private static final BigInteger INFLATED_BIGINT = BigInteger.valueOf(INFLATED);

// 如果此bigdecimal的有效位的绝对值小于或等于@code long.max,则该值可以紧凑地存储在此字段中并用于计算。
private final transient long intCompact;

// 所有18位十进制字符串都适合一个long;并非所有19位字符串都适合
private static final int MAX_COMPACT_DIGITS = 18;

构造器

 BigDecimal(int)           
 BigDecimal(double)       
 BigDecimal(long)         
 BigDecimal(String)    

示例

BigDecimal n = new BigDecimal(1);
BigDecimal n1 = new BigDecimal(2l);
BigDecimal n2 = new BigDecimal(2.01);
BigDecimal n3 = new BigDecimal("2.011");
BigDecimal n4 = BigDecimal.valueOf(2.02132);
System.out.println(n); // 1
System.out.println(n1); // 2
System.out.println(n2); // 2.0099999999999997868371792719699442386627197265625
System.out.println(n3); // 2.011
System.out.println(n4); // 2.02132

如果想要获取精确的值,最好使用BigDecimal(String),BigDecimal.valueOf中是现将double转string,然后调用BigDecimal(String),即BigDecimal(Double.toString(val))。

BigDecimal加减乘除也是调用对应的实例方法

BigDecimal n5 = n.add(n1);
BigDecimal n6 = n.subtract(n2);
BigDecimal n7 = n.multiply(n1);
BigDecimal n8 = n.divide(n1);

scale()和precision()

BigDecimal d1=new BigDecimal("1232400");
BigDecimal d2=new BigDecimal("123.1300");
BigDecimal d3=new BigDecimal("12.11");
System.out.println(d1.scale()); // 0
System.out.println(d2.scale()); // 4
System.out.println(d3.scale()); // 2
System.out.println(d1.precision()); // 7
System.out.println(d2.precision()); // 7
System.out.println(d3.precision()); // 4

之前感觉看代码的时候并没有理解scale和precision的区别,按照这个输出结果,scale输出的是小数位数,precision输出到底是什么,还有点蒙圈。

BigDecimal d1 = new BigDecimal("123.4500");
BigDecimal d2 = d1.stripTrailingZeros();
System.out.println(d1.scale()); // scale 4 precision 7 stringCache 123.4500 intCompact 1234500
System.out.println(d2.scale()); // scale 2 precision 0 stringCache 123.45 intCompact 12345

BigDecimal d3 = new BigDecimal("1234500");
BigDecimal d4 = d3.stripTrailingZeros();
System.out.println(d3.scale()); // scale 0 precision 7 stringCache 1234500 intCompact 1234500
System.out.println(d4.scale()); // scale -2 precison 0 stringCache 1.2345E+6 intCompact 12345

stripTrailingZeros将BigDecimal格式化为去掉末尾的0但是大小相等的BigDecimal。

BigDecimal d1 = new BigDecimal("0.32132");
BigDecimal d2 = d1.setScale(2, BigDecimal.ROUND_DOWN); // 0.32
BigDecimal d3 = new BigDecimal("-0.32132");
BigDecimal d4 = d3.setScale(2, BigDecimal.ROUND_DOWN); // -0.32
BigDecimal d5 = d1.setScale(2, BigDecimal.ROUND_CEILING); //0.33
BigDecimal d6 = d3.setScale(2, BigDecimal.ROUND_CEILING); // -0.32
BigDecimal d7 = d1.setScale(2, BigDecimal.ROUND_FLOOR); //0.32
BigDecimal d8 = d3.setScale(2, BigDecimal.ROUND_FLOOR); // -0.33
BigDecimal d11=new BigDecimal("0.366");
BigDecimal d12=new BigDecimal("-0.366");
BigDecimal d13=new BigDecimal("0.322");
BigDecimal d14=new BigDecimal("-0.322");
BigDecimal d15=d11.setScale(2,BigDecimal.ROUND_HALF_DOWN); // 0.37
BigDecimal d16=d12.setScale(2,BigDecimal.ROUND_HALF_DOWN); // -0.37
BigDecimal d17=d13.setScale(2,BigDecimal.ROUND_HALF_DOWN); // 0.32
BigDecimal d18=d14.setScale(2,BigDecimal.ROUND_HALF_DOWN); // -0.32
BigDecimal d19=d11.setScale(2,BigDecimal.ROUND_HALF_EVEN); // 0.37
BigDecimal d20=d12.setScale(2,BigDecimal.ROUND_HALF_EVEN); // -0.37
BigDecimal d21=d13.setScale(2,BigDecimal.ROUND_HALF_EVEN); // 0.32
BigDecimal d22=d14.setScale(2,BigDecimal.ROUND_HALF_EVEN); // -0.32
BigDecimal d23=d11.setScale(2,BigDecimal.ROUND_HALF_UP); // 0.37
BigDecimal d24=d12.setScale(2,BigDecimal.ROUND_HALF_UP); // -0.37
BigDecimal d25=d13.setScale(2,BigDecimal.ROUND_HALF_UP); // 0.32
BigDecimal d26=d14.setScale(2,BigDecimal.ROUND_HALF_UP); // -0.32
BigDecimal d27=d11.setScale(2,BigDecimal.ROUND_UP); // 0.37
BigDecimal d28=d12.setScale(2,BigDecimal.ROUND_UP); // -0.37
BigDecimal d29=d13.setScale(2,BigDecimal.ROUND_UP); // 0.33
BigDecimal d30=d14.setScale(2,BigDecimal.ROUND_UP); // -0.33
BigDecimal d31=new BigDecimal("0.325");
BigDecimal d32=new BigDecimal("-0.325");
BigDecimal d33=d31.setScale(2,BigDecimal.ROUND_HALF_EVEN); // 0.32
BigDecimal d34=d32.setScale(2,BigDecimal.ROUND_HALF_EVEN); // -0.32
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019年08月24日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • BigInteger
  • BigDecimal
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档