Java和Python中的整数除法,取余,舍入

关于除法,你也许以为没什么值得谈论的,毕竟小学的时候体育老师就教过咱们了。然而对于编程中使用的除法,我以为仍是有不少值得注意的细节的。为何我想深究一下?由于我平常主要使用Java和Python编程,而它们的除法在细节上有不少不一样之处,全是坑啊…因此接下来我也将着重于Java和Python,可是相信我,就算你不用Java和Python,也会有所收获的。html

1.整数除法

对两个不能整除的整数作除法,就要面对舍入的问题。和大多数编程语言同样,Java的基本策略是向零取整(round to zero),也就是向绝对值变小的方向取整。举几个香甜的小栗子:3/2=1, -3/2=-1。而对于Python而言,状况就有所不一样了。java

>>>-1/10
-1

显然若是按照Java的取整策略,-1/10应该得0,而Python给出的结果是-1。事实上Python的取整方式是向下取整,也就是向着数轴上负无穷的方向取整。
好吧,Java和Python的取整方式不一样,夺大点事儿啊…那么若是咱们要在Python下采用向零取整的结果,咋整?一种比较直接的方式是:python

>>>int(float(-1)/10)
0

2.取余

谁说没大事?( ̄▽ ̄)大事来了!编程

Java和Python整数除法都遵循下面这个公式:api

(a/b)*b+c=a

也就是说:oracle

a mod b=c=a-(a/b)*b

这里的/表示的是整数除法。既然它们的取整方式不同,那么取余也会受到影响:编程语言

For Java: -2 % 3==-2
For Python: -2 % 3==1

在某些实际应用中,咱们可能会被要求获得一个整数的各位数字。若是输入的整数的正的,Java和Python均可以用相同的方法来解决: 函数

def func(a):
    pos, res=1, []
    while a/pos:
        res+=(a/pos)%10,
        pos*=10
    return res

Java代码也差很少就是这样了。但若是输入的整数是一个负数,Java版本的代码仍是能够获得正确的结果,而Python不能(曾经在这里被坑的,举手)。那怎样用Python正确地搞定这个问题嘞?能够先去绝对值和符号,当正数来处理,最后再在结果里搭上符号。lua

3. Follow-ups

3.1 Python中的另外一个除法操做

咱们知道,在Python中,基本的除号“/”是被重载了的。当两个操做数都是整数时,进行整数除法,获得整数结果,不然进行浮点数除法(真除法),获得浮点数结果。从Python 2.2开始,另外一个除号被引入://,它只执行整数除法。注意,//的结果类型依操做数而定。code

>>>1.0/2
0.0
>>>1.0//2.0
0.0
>>>1//2
>0

另外,若是想同时获得商和余数,可使用内建的函数divmod,结果是一个tuple。

>>>divmod(7, 2)
(3, 1)
>>>divmod(7.0, 2)
(3.0, 1.0)

3.2 Python中的舍入

除了缺省的舍入方式,Python还有多种舍入可供选择。
Floor rounding:

>>>import math
>>>math.floor(1.2)
1.0
>>>math.floor(-1.2)
-2.0

Ceiling rounding:

>>>math.ceil(1.2)
2.0
>>>math.ceil(-1.2)
-1.0

Round-off:

>>>round(0.5)
1.0
>>>round(-0.4)
-0.0
>>>round(-0.5)
-1.0

内嵌的round函数也能够一个指定保留小数位数的参数:

>>>round(0.21, 1)
0.2
>>>round(0.21, 2)
0.21

Caution !

>>>round(2.675, 2)
2.67

咦?bug啦?!固然不是。这里要明确一件事:计算机只认识0,1(量子计算机?懵)。就是说,咱们输入的十进制数,在计算机内部都是用二进制来表示的。有的十进制数能够用二进制准确地表示出来,好比十进制的0.125能够表示为0b0.001;然而不少的小数是无法用二进制数精确表示的,计算机里存储的是它们的近似值,例如十进制的0.1,用二进制表示,能够近似为: 0b0.00011001100110011001100110011001100110011001100110011010,因此当咱们把它换回十进制数以输出或者使用,获得的值就是0.1000000000000000055511151231257827021181583404541015625。也就是说,0.1在计算机里并非恰好等于1/10的。你看:

>>>0.1+0.2
0.30000000000000004

一样,当咱们运行round()函数,也是对计算机中实际存储的值近似取舍。2.67实际上近似为2.67499999999999982236431605997495353221893310546875,第三位小数是4,那么round(2.675, 2)就至关于round(2.674, 2),结果固然是2.67。值得注意的是,这种现象是普遍存在于各类计算机和各类编程语言的,不是bug,只是有的语言选择了不让你看到。

3.3 Java中的舍入

Java提供了floor和ceil方法来实现向下和向上取整。

Math.floor(2.9)
Math.ceil(2.1)

这俩函数简单方便,居家旅行必备。另外Java中也有个round函数,能够实现各类复杂的取整。

System.out.println(Math.round(0.5));
//输出 1
System.out.println(Math.round(-0.5));
//输出 0
System.out.println(Math.round(-0.51));
//输出 -1

这什么鬼!Keep Calm and Carry On!
数学上有多种不一样的策略来进行取整,好比咱们体育老师教的四舍五入。各类取整策略的共同点就是要作真值做近似,那就会引入误差。四舍五入显然并非一种公平的策略(想一想0~4的舍和5~9的得)。
有一个叫作银行家舍入(Banker’s Rounding)的东西,不造你听过没,反正我是最近才知道的。事实上.NET和VB6都是默认采用这种方式,并且IEEE 754默认采用这种Rounding。Banker’s Rounding 也就是 round to even 策略。
假设当前考虑那位的数字是d(其实d就是将不被保留的第一位),若是d<5,则舍(round to zero);若是d>5,则入(round away from zero);而当d==5时,就要根据d先后的数位来肯定往哪边取了。

1) 若是d以后存在非零的数位,则入;
2)若是d以后不存在非零的数位,则看d以前的一个数位,用c表示:
  a.若是c是奇数,则入;
  b.若是c是偶数,则舍。

再来一把栗子,对下列数保留0位小数,
第一位小数就是d,整数位就是c:

BankRound(0.4)==0,  BankRound(0.6)==1,  BankRound(-0.4)==0,  BankRound(-0.6)==-1
BankRound(1.5)==2.0,  BankRound(-1.5)==-2.0,  BankRound(2.5)==2.0,  BankRound(-2.5)==-2.0
BankRound(1.51)==2.0,  BankRound(-1.51)==-2.0,  BankRound(2.51)==3.0,  BankRound(-2.51)==-3.0

能够看出,Banker’s Rounding对正数和负数的处理是对称的,所以不会引入符号带来的误差。另外它以均等的概率来舍入数位(考虑c, c有各一半的概率为奇数和偶数),因此屡次舍入后与真值的差异会较小。

扯了这么多,跟Java的Math.round( )有什么关系呢?我也是写到这才发现,好像没什么软(luan)关系。由于它并无遵循Banker’s rounding。而是按照如下策略进行取整:
当考虑的数位d不是5,d<5就舍,d>5则入。
当d==5:

a.若是d的右边有非零数位,则入;
b.若是d的右边没有非零数位,则 round to ceiling,即对负数舍,对正数入。

Java文档里是这么表述的

还有还有, 在Java里可使用 BigDecimal RoundingMode 实现更通用的取整方式。

double d=-2.5;
BigDecimal bd=new BigDecimal(d);
double nd=bd.setScale(0,
RoundingMode.HALF_EVEN).doubleValue();
System.out.println(nd);
//输出 -2.0

setScale 的第一个参数是保留的小数位数,第二个参数是舍入模式。可选的舍入模式有:
HALF_EVEN, 也就是银行家方式;
HALF_UP, 四舍五入;
HALF_DOWN, 五舍六入;
CEILING、FLOOR, 向正无穷、负无穷方向;
UP、DOWN*, 向零和远离零;
UNNECESSARY, 断言舍入后的值和原值相等,也就是不须要舍入。若是断言错了,抛出ArithmeticException异常。

先写到这,比较粗糙,可是但愿你有所收获吧。欢迎讨论,有话好好说( ̄▽ ̄)

转载请注明:做者曾会玩

相关文章
相关标签/搜索