第二篇,慢慢来

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

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

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

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

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

  1. //
  2. public BigDecimal(char[] in) {
  3. this(in, 0, in.length);
  4. }
  5. //
  6. public BigDecimal(char[] in, MathContext mc) {
  7. this(in, 0, in.length, mc);
  8. }
  9. //
  10. public BigDecimal(String val) {
  11. this(val.toCharArray(), 0, val.length());
  12. }
  13. //
  14. public BigDecimal(String val, MathContext mc) {
  15. this(val.toCharArray(), 0, val.length(), mc);
  16. }

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

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

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

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

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

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

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

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

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

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

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

    1.  
    2. 随机推荐

        1. php代理
        1. 有些网上接口请求需要用代理 php代码 <?php header('Access-Control-Allow-Origin:*'); $url=$_POST['urlString']; $res ...

        1. linux下安装部署ansible
        1. linux下安装部署ansible 介绍 Ansible是一种批量部署工具,现在运维人员用的最多的三种开源集中化管理工具有:puppet,saltstack,ansible,各有各的优缺点,其中sal ...

        1. Hashmap误区
        1. HashMap简介 HashMap 是一个散列表,它存储的内容是键值对(key-value)映射.HashMap 继承于AbstractMap,实现了Map.Cloneable.java.io.Ser ...

        1. BBS+Blog项目开发
        1. BBS+Blog项目开发 目前本项目已经上线,可以直接在GEEK浏览本项目效果:GEEK 1.项目需求 基于ajax和用户认证组件实现登录验证 基于ajaxform组件实现注册功能 系统首页文章列表 ...

        1. Django 启动源码
        1. handler = self.get_handler(*args, **options) run(self.addr, int(self.port), handler,ipv6=self.use_ip ...

        1. 2017-2018 ACM-ICPC, NEERC, Moscow Subregional Contest
        1. A. Advertising Strategy 最优策略一定是第一天用$y$元,最后一天再用$x-y$元补满. 枚举所有可能的$y$,然后模拟即可,天数为$O(\log n)$级别. 时间复杂度$O( ...

        1. Git服务器安装详解及安装遇到问题解决方案
        1. git是一个不错的版本管理的工具.现在自己在搞一个简单的应用程序开发,想使用git来进行管理.在Google了配置文档后,还是受了N多的挫折.某些文档质量不高,浪费了好多时间...... 好,切入正题 ...

        1. __x__(40)0909第五天__表格 table css 样式 美化
        1. 如果就向下面的代码那样,不写 tbody , 则浏览器自添加 tbody , 并将所有的 tr 移入 tbody 意味着 tr 并非 table 的子元素,而是 tbody 的子元素. 所以 以后编写 ...

        1. 在线协作开发工具apizza使用方法(https://apizza.net/projects)
        1. 1. 2. 3. 4. 5. 谷歌浏览器点击选择“更多工具”->“扩展程序”  6. 将刚才解压好的文件夹拖到这个位置即可

        1. 软件体系架构之ssh框架阅读笔记
        1. 首先我们要了解一下什么是ssh框架? SSH struts+spring+hibernate的一个集成框架,是目前比较流行的一种Web应用程序开源框架. ssh框架系统从职责上分为四层:web ...