1. ceil、floor和round的功能

首先,这三个方法都是Math类的静态方法,而且类Math在java.lang包下,所以我们在代码中可以直接调用Math的方法。

Math.ceil(double a)实现的是对小数向右取整,如 Math.ceil(-0.7) = -0.0,Math.ceil(0.5) = 1.0, Math.ceil(1.3) = 2.0

Math.floor(double a)实现的是对小数向左取整,如 Math.floor(-0.7) = -1.0,Math.floor(0.5) = 0.0, Math.floor(1.3) = 1.0

Math.round(double a)实现的逻辑是四舍五入,但是对于负数有点不一样,如 Math.round(-1.5) = -1,Math.round(-0.5) = 0,有点绕,所以为了好记点,等效为 Math.floor(a+0.5),而且返回的是整数。

2. ceil、floor源码

2.1 ceil和floor代码

因为round可通过floor来实现,所以round源码就不多加分析,主要分析ceil和floor方法,从下图我们可以知道ceil和floor方法实际上都是调用floorOrCeil方法实现,只是参数不一样。参数后续分析,所以我们接下来分析floorOrCeil这个方法。

2.2  floorOrCeil源码

看了源代码,有点复杂,接下来我们一段一段分析 

2.2.1 

int exponent = Math.getExponent(a);

这个方法是得到浮点数a的指数部分,这个指数不是我们的科学计算法中的以10为底的指数,这个指数是以2为底的指数。不懂就举例,如82.2的指数为6,0.23的指数为 -3,-0.1的指数为 -4,-6.3的指数为2,这个是怎么算的?

不知道怎么算出来的,那我们反推一下这些数字用指数怎么表示的,如下图,不知道你们能不能看懂,如果看不懂慢慢分析一下吧,讲也不太好讲。

2.2.2 

在知道指数怎么算出来后,我们应该知道指数小于0的情况是什么了,其实就是浮点数范围处于 -1<a<1,接下来用到了三目运算符 a ?b:c,如果a==0.0,注意 -0.0=0.0, 直接返回a ,如果不是0.0,判断是负数还是整数,如果是负数取负边界,如果是正数,取正边界。这个时候用到了我们前面提到的floorOrCeil参数问题,我们先考虑如果是ceil方法,ceil方法调用floorOrCeil,传入的负边界是 -0.0,正边界是1.0,那么负数取负边界得到 -0.0,整数取正边界得到 1.0,刚好是向右取整;如果是floor调用floorOrCeil,传入的负边界是 -1.0,正边界是0.0,负数取负边界 -1.0,整数取正边界 0.0,刚好是向左取整。

2.2.3 

如果指数大于52会怎么样呢?这里涉及浮点数的底层存储了,接下来请仔细阅读,如下图。

一个double浮点数8个字节64位,一位符号位,11位存放指数值,52位存放数值,不懂就举例。比如76.3,十进制小数转为二进制不懂的可以看我另一篇博客,76.3转为二进制是 100 1100.01001 1001 1001... (1001一直循环),和以10为底的指数原理一样,这个数字以2为底的指数知道是几位吗?对的,指数段是6,使用了指数,以2为底的指数,我们用P来表示,那么76.3就变为了 1.00110001001 1001 1001...P6。然后接下来76.3的64位是怎么存储的呢?符号位是0,指数是6,但是还要加上 1023,也就是1029,指数段二进制表示是 10000000101,然后数值段记录的数据是小数位,也就是 1.00110001001 1001 1001...E6截取 00110001001 1001 1001... 部分,要完整表示52位的话,数值段二进制则是 0011 0001 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011,至此,我们得到了76.3的二进制表示,接下来我们通过代码验证一下,如下。

现在我们来谈谈如果指数位大于等于52会怎么样?当指数位等于52时,那么数值段存的内容都是整数部分的二进制,小数部分根本没有存,所以会有官方注释提的“a value so large it must be integral”,也就是说这个浮点数小数部分在52位数值段得不到存储,所以认为是整数,那么就直接return。

2.2.4

“assert exponent >= 0 && exponent <= 51;”没什么好说的,进一步判断指数位是否处于0和51之间。从if语句中的mask和doppel相与和0比较,我们可以推断程序是想判断浮点数的小数部分是不是都是0,如果小数部分是0,我们可以直接返回。经过上面对浮点数的存储分析,我们怎么得到浮点数的小数部分呢?

首先,小数部分在数值段存储,但是指数部分也在数值段中存储,因为我们可以得到指数部分的位数,这样我们就可以通过向左移位把指数段的11位和数值段的指数部分移除,注意:符号位是不会被移除的。所以如果是负数的话,我们在移位结束之后还得把二进制的首位符号位去掉。

源码中首先通过Double.doubleToRawLongBits(a)求得浮点数的二进制表示 0100 0000 0101 0011 0001 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011,然后将DoubleConsts.SIGNIF_BIT_MASK >> exponent向右移动,其中DoubleConsts.SIGNIF_BIT_MASK是个常量,数值大小如下图,exponent是6。因此得到二进制表示 0000 0000 0000 0000 0011 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111。然后进行 与 运算,最后得到浮点数的小数部分二进制表示为 0000 0000 0000 0000 0001 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011,然后再和0比较,如果相等,说明小数部分是0,那么说明这个浮点数其实是个整数,直接return。

2.2.5

终于来到了最后,上面计算得到小数部分不是0,那我们需要先得到浮点数的整数部分,然后再考虑是向右取整还是向左取整 。先前求得的mask小数部分的二进制位全是1,现在取反则是符号位、指数段和数值段指数部分的二进制是1,那么 (~mask)的二进制表示是 1111 1111 1111 1111 1100 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000,和doppel进行与运算得到 0100 0000 0101 0011 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000,该二进制表示为76.0。紧接着判断是ceil还是floor,如果是ceil,那么sign是1.0,然后如果浮点数是负数,我们刚刚取整相当于已经向右取整,只考虑正数,所以sign*a>0.0,整数+1(sign是 1.0);如果是floor,那么sign是 -1.0,然后如果浮点数是正数,我们刚刚取整相当于已经向左取整,只考虑负数,所以sign*a>0.0,整数-1(sign是 -1.0),到此源码分析结束。

3. 自己重写实现ceil和floor以及round方法

我们自己重写的话,主要是取整那一块代码可以修改,其它的比较简单容易理解也不需要重写。所以,我们有什么方法可以取出浮点数的整数部分呢?

public static double ceilOrFloor(double a, double negative, double positive, double sign){int exp = Math.getExponent(a);if(exp<0){return (a==0.0) ? a : (a<0.0 ? negative : positive);}else if(exp>51){return a;}String s = String.valueOf(a);String s2 = s.substring(0, s.indexOf('.'));double result = Double.valueOf(s2);if(a==result){return a;}else{if(sign*a > 0.0)result += sign;return result;}}public static double floor(double a){return ceilOrFloor(a, -1.0, 0.0, -1.0);}public static double ceil(double a){return ceilOrFloor(a, -0.0, 1.0, 1.0);}public static long round(double a){return (long)floor(a+0.5);}

部分测试结果如下

Java,Math类中的ceil、floor和round函数源码解析以及自己重写实现相关推荐

  1. Java Math类静态double ceil(double d)与示例

    数学班双胞胎(double d) (Math Class double ceil(double d)) This method is available in java.lang package. 此 ...

  2. Java Math 类中的新功能--浮点数

    Java™语言规范第 5 版向 java.lang.Math和 java.lang.StrictMath添加了 10 种新方法,Java 6 又添加了 10 种.这个共两部分的系列文章的 第 1 部分 ...

  3. java Math类中的pow方法的问题Math.pow(64,1/3)是1而不是4

    2019独角兽企业重金招聘Python工程师标准>>> 因为Math.pow(x,y)这个函数是求x的y次方,x,y的值都是浮点类型的,而你现在要求的是pow(64,1/3),那么也 ...

  4. Java交互界面实现计算器开发设计【附函数源码,Java理论知识

    setOutput = "";setOutput +=INumSecond;//return setOutput;} } .[](https://gitee.com/vip2048 ...

  5. Java交互界面实现计算器开发设计【附函数源码】

    使用Java开发的简易计算器,包括加.减.乘.除.平方.立方.小数等运算,适合学习GUI编程实践, 目录 控件触发时集中处理函数 操作符点击后数据转换赋值函数 计算器窗口界面函数 运算符控件 数字控件 ...

  6. Java交互界面实现计算器开发设计【附函数源码】,java反射原理hash

    在这里我们需要首先定义私有的全局变量来接收在计算器运行过程中的一些变量,如输入的数值.小数点.操作符.输出框内容.计算结果等,因此对于在计算器使用过程中可能出现的变量要对其进行一一接收.在这里我们使用 ...

  7. 简述Math类中的常用方法,包括ceil,floor,min,max,round,random,double等方法的概述

    Math类中的常用函数: 1.ceil和floor函数: ceil函数:ceil(x)返回的是大于x的最小整数 floor函数:floor(x)返回的是小于或等于x的最大整数 //结果依次返回5.0和 ...

  8. java中怎样定义实数_Java Math 类中的新功能,第 1 部分: 实数

    在这篇由两部分组成的文章中,Elliotte Rusty Harold 与您一起探讨经典 java.lang.Math 类中的"新"功能.第 1 部分主要讨论比较单调的数学函数.第 ...

  9. Java Math类的常用方法

    在 Java 中 Math 类封装了常用的数学运算,提供了基本的数学操作,如指数.对数.平方根和三角函数等.Math 类位于 java.lang 包,它的构造方法是 private 的,因此无法创建 ...

最新文章

  1. 在python中调用js或者nodejs要使用PyExecJs第三方包。
  2. [CF843D]Dynamic Shortest Path
  3. 推荐系统笔记(评价指标及效果)
  4. python教程:15种字符串操作方法
  5. Spring的@Scope的几种取值
  6. cannot mount database in EXCLUSIVE mode
  7. 2021-06-28获取,更新,删除DOM节点
  8. phpcms v9框架的目录结构分析
  9. 电力电子技术笔记(1)
  10. matlab中提取裂缝图像,灰度图像中裂缝自动识别和检测方法与流程
  11. 为知笔记导入html,为知笔记导入印象笔记
  12. PHP学习之字符串操作
  13. photoshop中魔棒使用方法
  14. 1001 害死人不偿命的(3n+1)猜想 (15分)_Quentin
  15. Java面试考点思维导图
  16. CAN收发器TJA1050与MCP2551的对比
  17. 会议室预定管理系统_企业应用会议室智能预约管理系统有哪些好处
  18. (转载)[python学习笔记]Python语言程序设计(北理工 嵩天)
  19. WEB编程开发常用的代码
  20. pcb手动布线与自动布线

热门文章

  1. 精准营销的核心思维何在?
  2. ERP兵法——从案例透视方法(实施篇下)
  3. ABAP常见面试问题
  4. 无BOM禁止转生产订单
  5. 他手边有太多事要做了
  6. CONVERSION_EXIT_SDATE_OUTPUT
  7. centos8更换yum源_基于yum进行linux系统升级--从Centos7升级到Centos8
  8. java sl4j 日志_Java日志框架Slf4j+Log4j入门
  9. html如何在li里加a,li里面嵌套a标签html和css小例子
  10. 关于解决安装pwndbg问题sys.stderr.write(f“ERROR: {exc}“) /usr/bin/python3.5: No module named ensurepip