首先问一个问题,在Java中,将浮点型强制转换成整型,是做四舍五入、向下取整、还是向上取整?在回答这个问题之前我们先来了解一下Java中取整的几个函数。
Math.round(四舍五入)
static long round(double a);
static int round(float a);
Returns the closest long to the argument, with ties rounding to positive infinity.
如何理解上面这句话呢?特别是rounding to positive infinity这几个词?我们先来做个试验:
System.out.println(Math.round(-2.6)); //输出为-3
System.out.println(Math.round(-2.5)); //输出为-2
System.out.println(Math.round(-1.5)); //输出为-1
System.out.println(Math.round(-0.5)); //输出为0
System.out.println(Math.round(0.5)); //输出为1
System.out.println(Math.round(1.5)); //输出为2
System.out.println(Math.round(2.5)); //输出为3
其中对于正数的部分很好理解,令人费解的是-2.6的四舍五入是-3,而-2.5的四舍五入却是-2. 回过头来看round函数的注解:首先是返回与参数最接近的整数(returns the closest long to the argument);如果是.5,即有两个与参数最接近的整数,这个时候选择较大的那个(rounding to positive infinity)。
Java中还有另外一个四舍五入函数——rint,但它与round有些许不同:
static double rint(double a);
Returns the double value that is closest in value to the argument and is equal to a mathematical integer.
其实文档里关于rint的注解没写清楚,看到上面的注解,你的疑问肯定是如果出现0.5的情况呢?我们先来试验一下:
System.out.println(Math.rint(-2.6)); //输出为-3.0
System.out.println(Math.rint(-2.5)); //输出为-2.0
System.out.println(Math.rint(-1.5)); //输出为-2.0
System.out.println(Math.rint(-0.5)); //输出为-0.0
System.out.println(Math.rint(0.5)); //输出为0.0
System.out.println(Math.rint(1.5)); //输出为2.0
System.out.println(Math.rint(2.5)); //输出为2.0
为什么1.5和2.5都返回2.0?其实Java中的rint函数和python中的round类似,如果出现0.5的情况,返回的是偶数。
Math.ceil(向上取整)
static double ceil(double a);
Returns the smallest (closest to negative infinity) double value that is greater than or equal to the argument and is equal to a mathematical integer.
System.out.println(Math.ceil(-2.6)); //输出为-2.0
System.out.println(Math.ceil(-2.5)); //输出为-2.0
System.out.println(Math.ceil(-1.5)); //输出为-1.0
System.out.println(Math.ceil(-0.5)); //输出为-0.0 (可以发现浮点型的零是分正负的)
System.out.println(Math.ceil(0.5)); //输出为1.0
System.out.println(Math.ceil(1.5)); //输出为2.0
System.out.println(Math.ceil(2.5)); //输出为3.0
即在大于参数或者等于参数的整数中选择最小的一个。
Math.floor(向下取整)
static double floor(double a);
Returns the largest (closest to positive infinity) double value that is less than or equal to the argument and is equal to a mathematical integer.
System.out.println(Math.floor(-2.6)); //输出为-3.0
System.out.println(Math.floor(-2.5)); //输出为-3.0
System.out.println(Math.floor(-1.5)); //输出为-2.0
System.out.println(Math.floor(-0.5)); //输出为-1.0
System.out.println(Math.floor(0.5)); //输出为0.0
System.out.println(Math.floor(1.5)); //输出为1.0
System.out.println(Math.floor(2.5)); //输出为2.0
即在小于参数或者等于参数的整数中选择最大的一个。
强制转换
现在我们来看一下将double强制转换成int的时候,是如何取整的:
System.out.println((int)-2.6); //输出为-2
System.out.println((int)-2.5); //输出为-2
System.out.println((int)-1.5); //输出为-1
System.out.println((int)-0.5); //输出为0
System.out.println((int)0.5); //输出为0
System.out.println((int)1.5); //输出为1
System.out.println((int)2.5); //输出为2
发现强制转换不是四舍五入,不是向上取整,也不是向下取整,而是简单地去掉小数部分。
BigDecimal
BigDecimal
这个类中也有一个方法能够实现取整,但是又不局限于取整:
BigDecimal setScale(int newScale, int roundingMode);
BigDecimal setScale(int newScale, RoundingMode roundingMode);
Returns a BigDecimal whose scale is the specified value, and whose unscaled value is determined by multiplying or dividing this BigDecimal's unscaled value by the appropriate power of ten to maintain its overall value.
newScale
意思是保留位,设置为0意思就是取整,设置为1意思是保留一位小数。roundingMode
指定取整的方式,总共有8种:
RoundingMode | 注解 |
---|---|
UP | Rounding mode to round away from zero(远离0,向两边) |
DOWN | Rounding mode to round towards zero(向着0) |
HALF_UP | Rounding mode to round towards "nearest neighbor" unless both neighbors are equidistant, in which case round up(当出现0.5的情况,使用UP模式,即向两边) |
HALF_DOWN | Rounding mode to round towards "nearest neighbor" unless both neighbors are equidistant, in which case round down(当出现0.5的情况,使用DOWN模式,即向着0) |
HALF_EVEN | Rounding mode to round towards the "nearest neighbor" unless both neighbors are equidistant, in which case, round towards the even neighbor(当出现0.5的情况,取偶数) |
CEILING | Rounding mode to round towards positive infinity(与ceil函数相同,向着正方向) |
FLOOR | Rounding mode to round towards negative infinity(与floor函数相同,向着负方向) |
UNNECESSARY | Rounding mode to assert that the requested operation has an exact result, hence no rounding is necessary(不需要做舍入,如果对需要做舍入操作的数指定此模式,则会抛出 ArithmeticException。例如,保留位为0的时候,0.5会抛出异常,而1.0不会) |
其他
在搜索round函数的相关资料的时候,发现了python中一个关于round函数的很有意思的问题:
print(round(2.675, 2) #输出为2.67
前面有说到python中的round函数在遇到0.5的时候(其实是对半分的时候),会选择偶数,但是为什么上面的代码会给出2.67呢?我们可以在python的文档中找到答案:
**Note : **The behavior of
round()
for floats can be surprising: for example,round(2.675, 2)
gives 2.67 instead of the expected 2.68. This is not a bug: it’s a result of the fact that most decimal fractions can’t be represented exactly as a float.
原来是精度缺失导致的,那在Java中有没有这个问题呢:
BigDecimal BD = new BigDecimal(2.675);
System.out.println(BD.setScale(2,BigDecimal.ROUND_HALF_EVEN).toString()); //输出也是2.67
System.out.println(BD); //输出为2.67499999999999982236431605997495353221893310546875
通过试验,我们发现Java中也有相同的问题,也就是说我们输入的虽然是2.675,但是用二进制保存的时候因为无法精确表示,实际上存储的值是2.67499999999999982236431605997495353221893310546875。