负整数的整除与取余运算

转载 2014年02月19日 22:27:16

除法的取整分为三类:向上取整、向下取整、向零取整。 

1.向上取整:向+∞方向取最接近精确值的整数。在这种取整方式下,5 / 3 = 2, -5 / -3 = 2, -5 / 3 = -1, 5 / -3 = -1 
2.向下取整:向-∞方向取最接近精确值的整数。在这种取整方式下,5 / 3 = 1, -5 / -3 = 1, -5 / 3 = -2, 5 / -3 = -2 
3.向零取整:向0方向取最接近精确值的整数,换言之就是舍去小数部分,因此又称截断取整。在这种取整方式下,5 / 3 = 1, -5 / -3 = 1, -5 / 3 = -1, 5 / -3 = -1

通过观察可以发现,无论是向上取整还是向下取整,(-a)/b==-(a/b)都不一定成立。这给程序设计者带来了极大的麻烦。而对于向零取整,(-a)/b==-(a/b)是成立的,以此,C/C++(包括Java)采用这种取整方式。

而Python采用的是向下取整的方式



  C/C++ Python 精确值
-14/3 -4 -5 -4.67
-14%3 -2 1 /
14/-3 -4 -5 -4.67
14%-3 2 -1 /
-14/-3 4 4 4.67
-14%-3 -2 -2 /

总结规律如下:

  1. 两种语言中,商和余数都符合 被除数=商x除数+余数 这一数学规律。
  2. 两种语言中,整除的方法不同:C/C++ 是向零取整(负数向上、正数向下取整),Python 是下取整

以 n/3 和 n%3 为例,看看这两种处理方法的区别。

C 的情况,两种运算结果都关于0对称和反号

n -5 -4 -3 -2 -1 0 +1 +2 +3 +4 +5
-1 -1 -1 0 0 0 0 0 1 1 1
余数 -2 -1 0 -2 -1 0 1 2 0 1 2

Python 的情况,运算结果是完全连续的:

n -5 -4 -3 -2 -1 0 +1 +2 +3 +4 +5
-2 -2 -1 -1 -1 0 0 0 1 1 1
余数 1 2 0 1 2 0 1 2 0 1 2

那么,为何Python整除运算采用向下取整的规则,详细内容在Why Python's Integer Division Floors?,简单地来讲就是:

因为python认为余数r用到的机会会更大,采用向下取整的规则可以保证余数r与除数b的符号相同(同正或者同负)。以下为重点内容的摘抄:


假设a和b都>=0时,

b * q + r = a, 0 <= r < b

如果希望将这一关系扩展到a为负(b仍为正)的情况,有两个选择:一是q向0取整,r取负值,这时约束关系变为 0 <= abs(r) < b,另一种选择是q向下(负无穷方向)取整,约束关系不变,依然是 0 <= r < b。

在数学的数论中,数学家总是倾向于第二种选择(参见如下Wikipedia链接)。

在Python语言中也做了同样选择,因为在某些取模操作应用中被除数a取什么符号并不重要。

例如从POSIX时间戳(从1970年初开始的秒数)得到其对应当天的时间。因为一天有24*3600 = 86400秒,这一操作就是简单的t % 86400。但是当表达1970年之前的时间,这时是一个负数,向0取整规则得到的是一个毫无意义的结果!而向下取整规则得到的结果仍然是正确的。 
另外一个我能想到的应用是计算机图形学中计算像素的位置。我相信这样的应用还有更多。

顺便说一下,b取负值时,仅需要把符号取反,约束关系变为: 
0 >= r > b 
那么,现在的问题变成,C为啥不采取(Python)这样的选择呢?可能是设计C时硬件不适合这样做,所谓硬件不适合这样做是说指那些最老式的硬件把负数表示为“符号+大小”而不是像现在的硬件用二进制补码表示(至少对整数是用二进制补码)。我的第一台计算机是一台Control Data大型机,它用1的补码来表示整数和浮点数。60个1的序列表示负0! 
Tim Peters对Python的浮点数部分洞若观火,对于我想把这一规则推广到浮点数取模运算有些担心。可能他是对的,因为向负无穷取整的规则有可能导致当x是绝对值特别小的负数时x%1.0会丢失精度。但是这还不足以让我对整数取模,也就是//进行修改。 
附言:注意我用了//而不是/,这是一个Python 3 语法,而且在Python 2 中也是有效的,它强调了使用者是要进行整除操作。Python 2 中的 / 有可能产生歧义,因为对两个操作数都是整数时或者一个整数一个浮点数或者两个都是浮点数时,返回的结果类型不同。




来自:http://cuihao.is-programmer.com/posts/38553.html
http://www.cnblogs.com/zijin/p/3468802.html

vb.net中的整除

vb.net中的整除C#中的除法的写法应该很简单,不过vb.net中的除法就不是简单的一个 / 了,在vb.net中存在两种除法的写法,一种是 / 普通除法的写法它允许小数的存在而且会自动的四舍五入,...

C++(C#)取余和取模运算

  • 2017年12月01日 10:49
  • 650KB
  • 下载

整数位操作比除法/取余快多少?

前言最近在写代码的时候,在CSDN论坛上看到一段测试代码,发现位操作求余比mod快了不是一点啊。所以借别人的代码小小总结一下。代码块用时测试代码,例如: #include #include #in...

大整数求组合数取余(Lucas定理)

【卢卡斯(Lucas)定理】 Lucas定理用来求C(a,b)mod p的值,其中p为素数。 数学表达式为: Lucas(a,b,q)=C(a%q,b%q)*Lucas(a/p,b/p,p); ...

大整数除法2(取模和取余) 有改进,但依旧过不了 UVa 10494

对上一版本的改进地方在于,用减法代替除法时,直接将减数补0对齐被减数(参考北大那本书上提到的算法,但依旧没过了。。) Code: //两个大整数相除和取余。有改进,但依旧过不了 #include...

正数(十进制)变二进制 除以2取余 小数(十进制)变二进制 乘以2取整数

十进制整数转二进制: 就是把该十进制数,用二因式分解,取余。 以12为例,转为二进制 2除以12得6,余0,取0 2除以6得3,余0,取0 2除以3得1,余1,取1 最后剩下1,由它开始写起,就可得...

关于快速幂取余运算问题

p次方求和 时间限制:1000 ms  |  内存限制:65535 KB 难度:3 描述 一个很简单的问题,求1^p+2^p+3^p+……+n^p的和。 输入第一行单独一个数字t表示测试数...

位运算总结 取模 取余

位运算应用口诀 清零取反要用与,某位置一可用或 若要取反和交换,轻 轻松松用异或移位运算 要点 1 它们都是双目运算符,两个运算分量都是整形,结果也是整形。 2 “”右移:右边的位...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:负整数的整除与取余运算
举报原因:
原因补充:

(最多只允许输入30个字)