第二篇,慢慢来

根据指数调整有效小数位数

    // 上一篇由字符串创建BigDecimal代码中,有部分代码没有给出,这次补上
   // 这个是当解析字符数组时存在有效指数时调整有小小数位数方法
private int adjustScale(int scl, long exp) {// 参数:scl-->>原生有效小数位数,exp:有效指数
long adjustedScale = scl - exp;// 这个其实自己写个例子就能明白,例如数值1.234*10^-3次方,此时实际数值位0.001234scl=3,exp=-3,
                           // 实际有效小数位数是不是3-(-3)=6
if (adjustedScale > Integer.MAX_VALUE || adjustedScale < Integer.MIN_VALUE)// 前提是这个scl-exp还在int的表数范围,否则抛出异常
throw new NumberFormatException("Scale out of range.");
scl = (int) adjustedScale;// 若还在int的表数范围则强转为int并返回
return scl;
}

解析字符数组中的指数表达式

 // 解析指数表达式
private static long parseExp(char[] in, int offset, int len){
long exp = 0;// 先定义一个指数值exp
offset++;// 将索引自增,在调用该方法时,索引尚还在指数标识符'e'/'E'这里,索引需要自增,不清楚的看上一篇文章
char c = in[offset];// 获取当前字符c,并将需要解析的字符长度自减
len--;
boolean negexp = (c == '-');// 判断指数表达式的首位是不是符号位,并以negexp标识位表示是否为负
// optional sign
if (negexp || c == '+') {// 首位就是符号位,不是'-'就是'+'
offset++;// 解析出符号位,索引自增
c = in[offset];// 继续获取当前字符c,长度自减
len--;
}
if (len <= 0) // no exponent digits,若第一位不是符号位,但是长度已经小于等于0,就代表该指数表达式为空将抛出异常
throw new NumberFormatException();
// skip leading zeros in the exponent
while (len > 10 && (c=='0' || (Character.digit(c, 10) == 0))) {// 去除除符号位外的前置字符'0',因为并没有什么卵用
offset++;
c = in[offset];
len--;
}
if (len > 10) // too many nonzero exponent digits,若除'0'后指数位数还是大于10则抛出异常,一般指数都很少,
                //10^100:指数才3位,但是这个数量级可是比整个宇宙的行星数量还多
throw new NumberFormatException();
// c now holds first digit of exponent
for (;; len--) {// 需要解析的字符长度小于等于10进入循环
int v;// 定义当前循环中解析字符c的实际数值
if (c >= '0' && c <= '9') {// 若字符c是数字值,此时获取c的实际数字值并赋值给v
v = c - '0';
} else {
v = Character.digit(c, 10);
if (v < 0) // not a digit
throw new NumberFormatException();
}
exp = exp * 10 + v;// 计算当前已经解析出的指数表达式的值,因多解析出一位因此整个表达式的值:原指数值exp上升一个进位制需要扩大10倍
if (len == 1)// 若此时需要解析的长度已经是1则结束循环(此时当前字符的的位置尚未自减)
break; // that was final character
offset++;// 若还未解析到最后的字符则长度自减,并获取下一个字符并赋值给c
c = in[offset];
}
if (negexp) // apply sign,解析完成以后根据符号位negexp获取指数的实际值并返回
exp = -exp;
return exp;
}

BigDecimal的构造方法,这些构造包含推荐使用的以String为构造参数的方法最终调用的都是上篇文章所分析的以字符数组为参数的构造器

    //
public BigDecimal(char[] in) {
this(in, 0, in.length);
}
//
public BigDecimal(char[] in, MathContext mc) {
this(in, 0, in.length, mc);
}
//
public BigDecimal(String val) {
this(val.toCharArray(), 0, val.length());
}
//
public BigDecimal(String val, MathContext mc) {
this(val.toCharArray(), 0, val.length(), mc);
}

接下来看一下其它构造器,这些其实都不是推荐使用的构造器

    public BigDecimal(double val) {// 这个方法并不推荐使用,因为浮点数在计算机中听不一定能准确表示,因此使用该构造方法创建的实际值可能与传入参数的看到的表面值并不相同
this(val,MathContext.UNLIMITED);// 方法转发,调用下面的方法
} public BigDecimal(double val, MathContext mc) {
if (Double.isInfinite(val) || Double.isNaN(val))// 传入的参数为无穷大或者为非数,则抛出异常
throw new NumberFormatException("Infinite or NaN");
// Translate the double into sign, exponent and significand, according
// to the formulae in JLS, Section 20.10.22.
long valBits = Double.doubleToLongBits(val);// 该方法还没细看,作用就是在存储层面将double转变为long类型,double与long类型都是64位,但是double首位为符号位,
                                  接下来的11位为指数位,最后的52位为尾数位.而long类型64位都是尾数.这个方法就是将double不按照原有的解析逻辑解析,
                                  而是直接按照long类型解析
int sign = ((valBits >> 63) == 0 ? 1 : -1);// 判断最高位符号位的正负对sign进行赋值
int exponent = (int) ((valBits >> 52) & 0x7ffL);// 0x7ffL的二进制表示:低位11位均为1其余都是0,这一步就是获取double双精度浮点数的中间11位的指数部分
long significand = (exponent == 0
? (valBits & ((1L << 52) - 1)) << 1
: (valBits & ((1L << 52) - 1)) | (1L << 52));// valBits & ((1L << 52) - 1):用于获取double的尾数位
exponent -= 1075;// 自减1075,因为在原始的double指数位中存储的是指数实际值+1023,至于为什么可以取看看浮点数的底层原理,但是这里不仅减去了1023
                  // 又减去了52,这是因为double的实际值=((-1)^sign)*significand*2^exponent,而标准的表示中significand前默认是"1."
                  // 即significand全部都是小数,这也是为啥在指数不为0时进行"| (1L << 52)"操作.此时significand为long类型的整数也就是
                  // 上升了52个进位制,因此需要减去52,索引一共减去1075
// At this point, val == sign * significand * 2**exponent. /*
* Special case zero to supress nonterminating normalization and bogus
* scale calculation.
*/
if (significand == 0) {// 若处理完成后的尾数为0,这是特殊情况:此时BigDecimal值为0,有效位数为1
this.intVal = BigInteger.ZERO;
this.scale = 0;
this.intCompact = 0;
this.precision = 1;
return;
}
// Normalize
while ((significand & 1) == 0) { // i.e., significand is even,去除尾数右侧的0
significand >>= 1;// 若尾数为偶数则缩小二倍并将指数自增(实际值还不变)
exponent++;
}
int scale = 0;
// Calculate intVal and scale
BigInteger intVal;// 定义有效小数位数与intVal
long compactVal = sign * significand;
if (exponent == 0) {
intVal = (compactVal == INFLATED) ? INFLATED_BIGINT : null;
} else {
if (exponent < 0) {
intVal = BigInteger.valueOf(5).pow(-exponent).multiply(compactVal);
scale = -exponent;
} else { // (exponent > 0)
intVal = BigInteger.valueOf(2).pow(exponent).multiply(compactVal);// 根据double的实际值=((-1)^sign)*significand*2^exponent
                                                    // 获取实际值
}
compactVal = compactValFor(intVal);// 之前分析过就是根据intVal获取简洁值(前提是intVal对象的值可以使用long类型表示而不溢出)
}
int prec = 0;int mcp = mc.precision;// 定义有效位数及获取MathContext中的有效位数
if (mcp > 0) { // do rounding
int mode = mc.roundingMode.oldMode;
int drop;
if (compactVal == INFLATED) {// 代表intVal对象的实际值已经溢出
prec = bigDigitLength(intVal);// 见

JDK8 BigDecimal API-创建BigDecimal源码浅析二的更多相关文章

  1. ReentrantLock和condition源码浅析(二)

    转载请注明出处... 接着上一篇的ReentrantLock和condition源码浅析(一),这篇围绕着condition 一.condition的介绍 在这里为了作对比,引入Object类的两个方 ...

  2. SparkConf加载与SparkContext创建(源码阅读二)

    紧接着昨天,我们继续开搞了啊.. 1.下面,开始创建BroadcastManager,就是传说中的广播变量管理器.BroadcastManager用于将配置信息和序列化后的RDD.Job以及Shuff ...

  3. LinkedList类源码浅析(二)

    1.上一节介绍了LinkedList的几个基本的方法,其他方法类似,就不一一介绍: 现在再来看一个删除的方法:remove(Object o) remove方法接受一个Object参数,这里需要对参数 ...

  4. ArrayList类源码浅析(二)

    1.removeAll(Collection<?> c)和retainAll(Collection<?> c)方法 第一个是从list中删除指定的匹配的集合元素,第二个方法是用 ...

  5. 【深入浅出jQuery】源码浅析--整体架构

    最近一直在研读 jQuery 源码,初看源码一头雾水毫无头绪,真正静下心来细看写的真是精妙,让你感叹代码之美. 其结构明晰,高内聚.低耦合,兼具优秀的性能与便利的扩展性,在浏览器的兼容性(功能缺陷.渐 ...

  6. Android开发之Theme、Style探索及源码浅析

    1 背景 前段时间群里有伙伴问到了关于Android开发中Theme与Style的问题,当然,这类东西在网上随便一搜一大把模板,所以关于怎么用的问题我想这里也就不做太多的说明了,我们这里把重点放在理解 ...

  7. CountDownLatch源码浅析

    Cmd Markdown链接 CountDownLatch源码浅析 参考好文: JDK1.8源码分析之CountDownLatch(五) Java并发之CountDownLatch源码分析 Count ...

  8. Bytom侧链Vapor源码浅析-节点出块过程

    Bytom侧链Vapor源码浅析-节点出块过程 在这篇文章中,作者将从Vapor节点的创建开始,进而拓展讲解Vapor节点出块过程中所涉及的源码. 做为Vapor源码解析系列的第一篇,本文首先对Vap ...

  9. Phoenix创建索引源码过程

    date: 2020-09-27 13:50:00 updated: 2020-09-28 16:30:00 Phoenix创建索引源码过程 org.apache.phoenix.index.Inde ...

随机推荐

  1. Tomcat Getshell

    安装环境 账号密码路径:Tomcat6.0/conf/tomcat-users.xml 弱口令扫描工具 后台默认登陆地址:html://xx.xx.xx.xx/manager/html 后台war f ...

  2. .Net Core 部署到IIS

    微软官方教程: https://docs.microsoft.com/en-us/aspnet/core/publishing/iis?tabs=aspnetcore2x 在vs中创建.net cor ...

  3. Kali Linux常用服务配置教程DHCP服务原理

    Kali Linux常用服务配置教程DHCP服务原理 动态主机配置协议(Dynamic Host Configuration Protocol,简称DHCP)是一个局域网的网络协议,基于UDP协议工作 ...

  4. The Apache HBase™ Reference Guide

    以下内容由http://hbase.apache.org/book.html#getting_started节选并改编而来. 运行环境:hadoop-1.0.4,hbase-0.94.22,jdk1. ...

  5. 清除电脑缓存的bat文件

    电脑在使用了之后,会产生垃圾缓存,若不及时清理会降低电脑的运行速度. 1.步骤: 2.新建一个记事本文件,命名“系统清理”;(或其他名字) 3.原封不动复制下面的文字到该记事本中 @echo off ...

  6. 给出两个单词word1和word2,写一个函数计算出将word1 转换为word2的最少操作次数。

    问题: 给出两个单词word1和word2,写一个函数计算出将word1 转换为word2的最少操作次数. 你总共三种操作方法: 1.插入一个字符 2.删除一个字符 3.替换一个字符 格式: 输入行输 ...

  7. 根据两点经纬度计算距离和角度——java实现

    原理:见上一篇博客   http://blog.csdn.net/xiaobai091220106/article/details/50879365 百度地图拾取经纬度坐标:http://api.ma ...

  8. [LeetCode] Chalkboard XOR Game 黑板亦或游戏

    We are given non-negative integers nums[i] which are written on a chalkboard.  Alice and Bob take tu ...

  9. Servlet 文件上传

    Servlet 可以与 HTML form 标签一起使用,来允许用户上传文件到服务器.上传的文件可以是文本文件或图像文件或任何文档. 本文使用到的文件有: 创建一个文件上传表单 下面的 HTML 代码 ...

  10. spring boot中jsp解析c标签方法

    pro.xml中添加jstl标签 <dependency><groupId>javax.servlet</groupId><artifactId>jst ...