整数除法的行为是什么?

277
例如,
int result;

result = 125/100;
或者
result = 43/100;

结果是否总是除法的下取整?定义的行为是什么?


9
总结:有符号整数除法会朝着零方向截断。对于非负的结果,这与 floor(向负无穷舍入)相同。 (请注意,C89不保证这一点,请参见答案。) - Peter Cordes
58
大家一直在说“朝零截断”、“向上取整”或“向下取整”,就像代码在有意决定使用哪种技术一样。如果代码会说话,它会说:“我只是把小数部分扔进垃圾桶,然后继续前进”。 - Timothy L.J. Stewart
7
“代码”做出了一个有意的决定。根据规范,整数除法应该是T(截断)除法。因此,模数/余数运算符的实现方式与其他语言(例如Python或Ruby)中的不同。请参见[此处](https://en.wikipedia.org/wiki/Modulo_operation)以获取不同语言执行模数运算符的列表,以及[此处](https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/divmodnote-letter.pdf)中列出的至少五种常见编程语言决定执行div / modulo的方法。 - 13steinj
7
@13steinj,我说话比较口语化,因为评论中有些人会说“它向零截断...不,它是地板函数...不,如果是负数就是天花板函数...”有时技术细节并不能像我们希望的那样在人类记忆中传播到未来,但是知道“小数部分被舍去”这一点,你就可以推导出技术要点。技术细节是沉重的负担,但直觉就像风一样轻盈清新,我会将其传播得远远的,必要时我会知道从哪里开始。就像你链接的那篇论文,谢谢。 - Timothy L.J. Stewart
我在这里回答了(https://dev59.com/23E85IYBdhLWcg3wqleE#56201161),强调欧几里得除法的重要性(整数除法和模运算符之间的相互作用)。 - Picaud Vincent
相关:进行整数除法时,四舍五入到最近的整数,而不是截断:https://dev59.com/3XE95IYBdhLWcg3wKq2q#58568736 - Gabriel Staples
6个回答

234
结果不一定总是向下取整,而是朝0舍入。对于整数除法,/运算符的结果是代数商,任何小数部分都将被丢弃。如果商a/b可表示,则表达式(a/b)*b + a%b应等于a。这通常称为“朝零截断”。需要注意的两点是:1.操作数执行通常的算术转换。2./运算符的结果是第一个操作数与第二个操作数相除的商;%运算符的结果是余数。在这两个操作中,如果第二个操作数的值为零,则其行为是未定义的。[注:重点是我的]

27
除非你在将负数除以正数(或反之),否则结果会向下取整。 - Will A
86
它既不是地板也不是天花板,它是小数部分截断,概念上有所不同! - lornova
44
@Will A: 不是的。它被定义为朝零方向截断。给它起其他名称只会增加混淆,请不要这样做。 - Martin York
50
从数学角度来看,向零截断等价于“如果大于0,则取下整数,否则取上整数”。我认为仅称其为截断比称其为下取整/上取整更简单,但两者皆可。不过,无论如何,Will A 的观点是正确的:Dirkgently 的答案部分错误,因为他声称原帖作者关于结果为除法取下整数的说法是正确的。 - Brian
9
@Philip Potter:我不认为它在 C89 中被定义,也不在1998年的C++标准中。当然,在那些标准中,必须满足(a / b) * b + a % b == a,并且a % b的绝对值必须小于a,但是对于负数ab时,a % b是否为负数没有指定。 - David Thornley
显示剩余4条评论

55

Dirkgently在这篇回答中对C99中的整数除法给出了一个很好的描述,但你也应该知道,在C89中,带有负操作数的整数除法具有实现定义方向。

根据ANSI C草案(3.3.5):

如果任何一个操作数为负数,则“/”运算符的结果是代数商小于最大整数或者大于最小整数的整数是由实现定义的,而“%”运算符的结果的符号也是如此。如果商a/b可表示,则表达式(a/b)*b + a%b应等于a。

因此,在使用C89编译器时,当涉及到负数时要小心。

有趣的是,C99选择朝向零截断,因为这就像FORTRAN做的一样。请参见comp.std.c上此消息


4
C99草案N1256的前言第5段提到了“可靠整数除法”作为一种新的语言特性。厉害啊 *-* - Ciro Santilli OurBigBook.com
1
截断是大多数常见CPU硬件(例如x86)的行为方式,因此做出不同的选择是疯狂的。我不知道Fortran语义或硬件行为哪个先出现,但这也不是巧合,它们是相同的。 - Peter Cordes
3
大多数常见的 CPU 硬件可以比截断除法更快地执行大多数常量的向下取整除法。在我看来,如果标准规定 expr1 / expr2expr1 % expr2 在两个表达式都以相同方式组合相同对象时必须保持一致,对于 expr2 也是如此,但在截断除法与向下取整除法之间的选择上则没有具体规定,将允许生成更有效率的代码而不会破坏太多兼容性(实现者可以根据需要记录特定行为)。 - supercat

38

是的,结果总是朝零截断。它会朝最小的绝对值四舍五入。

-5 / 2 = -2
 5 / 2 =  2

对于无符号数和非负有符号数,这与floor函数相同(向负无穷方向舍入)。


9
是的,floor 函数只对正整数有效。 - Leonid

27

4
我同意这条评论,质疑(负%正)是否有用。另外我想问,某些情况下需要进行算术不正确行为的"unsignedvar > signedvar" 是否有用?我可以理解不要求总是正确行为的理由;但我看不出为何需要错误行为。 - supercat
8
一个优秀的参考资料,说明为什么向零取整是整数除法的正确行为(与 C 语言的定义相反,后者是有问题的且几乎从不有用的)。+1 - R.. GitHub STOP HELPING ICE
考虑:filtered = (k - 1) * filtered + value + carry; carry = filtered % factor; filtered /= factor,并通过改变value的值进行迭代。它可以很好地近似一阶低通滤波器,时间常数为k的整数...但只有在除法截断且carry获得负值时才是对称的。从时间到时间,两种除法行为都很方便。 - hobbs
1
@hobbs:我认为上面的代码在信号穿过零点时不会表现得很干净。如果div是一个向下取整的除法运算符,而factor是奇数,则对于所有小于INT_MAX-(factor div 2)的值,filtered += (filter+(factor div 2)) div factor将产生干净且对称的行为。 - supercat
@supercat,它确实有效;那段代码只是我在原子钟控制器中运行了一段时间的某些代码的轻微提炼。 - hobbs
@hobbs:对于任何给定的filteredcarryfactor值,都会有“因子”个值产生每个可能的输出值,这些输出值严格介于最小和最大可能的输出值之间,除了会有2*factor+1个值产生输出值为零。 - supercat

13

结果总是除法的下取整吗?

不是。结果会有所变化,但只有在负数的情况下才会发生变化。

定义行为是什么?

为了明确起见,下取整向负无穷大舍入,而整数除法向零舍入(截断)。

对于正数,它们是相同的。

int integerDivisionResultPositive= 125/100;//= 1
double flooringResultPositive= floor(125.0/100.0);//=1.0

对于负值,情况就不同了

int integerDivisionResultNegative= -125/100;//=-1
double flooringResultNegative= floor(-125.0/100.0);//=-2.0

4

我知道其他人已经回答了你的问题,但是通俗来说:

5 / 2 = 2 //因为5和2都是整数,整数之间的除法总是截断小数部分。

5.0 / 2 或 5 / 2.0 或 5.0 / 2.0 = 2.5 //这里5或2或两个数都有小数点,所以商将会是一个小数。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接