Java进阶--从源码理解Math.ceil()、Math.floor()、Math.round()

Math.ceil()

首先看看Math.ceil()定义:Math.ceil()是常见编程语言中的常用代码,ceil() 方法执行的是向上取整计算,它返回的是大于或等于函数参数,并且与之最接近的整数。下面特殊情况:

1.如果当前参数就是整数,直接返回。

2.如果当前参数不是数值型、无穷大,或者是正0和负0,直接返回当前参数。

3.如果当前参数小于0但是大于-1.0,则返回-0.  

例如: 
Math.ceil(12.2)//返回13.0

Math.ceil(12.7)//返回13.0

Math.ceil(12.0)// 返回12.0

Math.ceil(-0.8)//返回-0.0

然而光记这些概念是很容易遗忘的,如果能够弄懂Java api 到底是怎样实现,这样不但知其然,而且之前所以然,当然记忆的更加深刻。下面就一起走进Java  api  吧。下面就是Math.ceil() 在Java  api 中的实现代码。为了方便理解进行了一定的简化。

//ceil , floor 是 Math 类中的两个静态方法。
public final class Math {

  public static double ceil(double a) {
        return floorOrCeil(a, -0.0, 1.0, 1.0); //在这里进行了一定的简化,floorOrCeil原本不在Math类中。

    }

  public static double floor(double a) {
        return floorOrCeil(a, -1.0, 0.0, -1.0); //floor 也调用了floorOrCeil 方法
    }
}

原来 ceil 和 floor 方法都调用了一个 floorOrCeil();方法而且仅仅是在调用此方法的时候传入了不同的参数,下面就看看这个 floorOrCeil() 到底是什么。

private static double floorOrCeil(double a,
                                      double negativeBoundary,
                                      double positiveBoundary,
                                      double sign) {
   //                                   
        int exponent = Math.getExponent(a);
    //exponent 是指的a的指数。如果 a=1.2*2^9,那么a的指数就是9。
        if (exponent < 0) {
            /*
             * 如果a的指数小于0 那么可以肯定-1<a<1,
             * 此时如果a==0,则直接返回0,如果a<0,返回negativeBoundary
             *  在ceil 方法中negativeBoundary =-0.0
             * 在floor方法中negativeBoundary =-1.0
             */
            return ((a == 0.0) ? a :
                    ( (a < 0.0) ?  negativeBoundary : positiveBoundary) );

        } else if (exponent >= 52) {
            /*
             * exponent >= 52,则超出了double的范围,为无穷大,则直接返回。
             */
            return a;
        }
        // 如果 exponent >= 0 && exponent <= 51,在double范围内

        assert exponent >= 0 && exponent <= 51;

        long doppel = Double.doubleToRawLongBits(a);
        //获得a的二进制格式。
        long mask   = 0xfffffffffffff >> exponent;
        //mask 是获得a的小数位的掩码,mask & doppel获得a的小数位,如果a的小数位为0那么a是整数,则直接返回。
        if ( (mask & doppel) == 0L )
            return a; // 整数
        else {
        //result将a的小数部分去掉,获得a的整数。
            double result = Double.longBitsToDouble(doppel & (~mask));
        //sign=1,标识ceil调用,sign=-1,标识floor调用
            if (sign*a > 0.0)
                result = result + sign; 
                 //如果a的小数部分不为0且是ceil 方法调用时,如果a>0,则:返回result+1,a<0,直接去掉小数部分。
                 //如果a的小数部分不为0且是floor方法调用时,如果a>0,则:返回result,a<0,返回result-1。

            return result;

上面算法中重点要理解的是下面这行代码:

        long doppel = Double.doubleToRawLongBits(a);
        long mask   = 0xfffffffffffff >> exponent;
        if ( (mask & doppel) == 0L )
            return a; 
        else {
            double result = Double.longBitsToDouble(doppel & (~mask));
            if (sign*a > 0.0)
                result = result + sign; 
                }

其中 long doppel = Double.doubleToRawLongBits(a);返回a的 IEEE 754 浮点数运算标准的二进制比特流。在这里以一张图来了解 IEEE 754 浮点数运算标准:

这里写图片描述

如果a=101.1 则用 IEEE 754 标准表示为:
这里写图片描述
 指数大小为:1029-1023=6,其中:mask 的作用是用来获取小数部分的。 long mask   = 0xfffffffffffff >> exponent ; mask 只有小数位上对应的值为1,其余为0,mask & doppel 获取小数部分。 
 double result = Double.longBitsToDouble(doppel & (~mask)); 获取整数部分。

总结一下ceil算法的流程:其实上面的算法分为以下几个步骤:

1.如果 a大于-1且小于1,如a大于-1且小于0则返回-0.0,否则返回1。

2.判断a是否超出了double的返回,如果超过了则直接返回a。

3.如果a为整数直接返回a。

4.获得a的整数部分b,如果a>0,返回b+1,否则直接返回b。

5.结束。 

下面是算法的流程图:

这里写图片描述

最后一句话说一下Math.floor(),其实由上面的算法可以看出 Math.ceil(x)=-Math.floor(-x) 因此只要理解了ceil的实现方式 floor 就很容易理解了。

Math.round()

还是先直接上源码:

 public static int round(float a) {
        if (a != 0x1.fffffep-2f) // 如果a不等于最接近0.5且比0.5小的浮点数值
            return (int)floor(a + 0.5f);
        else
            return 0;
 }

上面代码很清楚其实 round(float a) 就是将a+0.5然后向下取整。例如:Math.round(11.5)=Math.floor(11.5+0.5)=12. Math.round(-11.5) =Math.floor(-11.5+0.5)=-11.

发布了64 篇原创文章 · 获赞 4 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/GracefulGuigui/article/details/103801431