首先看看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
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 就很容易理解了。
还是先直接上源码:
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.