计算机中负数除法取余问题分析与实现

计算机中负数除法取余问题分析与实现

    • 背景
    • 问题说明
    • 原理分析
    • 参考资料

背景


项目中遇到一个奇怪的问题,定位bug半天,发现同样的负数取余操作,在不同编程语言中实现竟然不一致,我滴个天,惊呆了。于是,仔细分析了下背后原因,小结如下。

问题说明


为便于说明,先举个实际例子:请你回答下,在C和Python语言下,-1 % 8运算结果各自是多少?

实际运行结果是:

-1 % 8 =>  -1    // c
-1 % 8 =>  7     // python

而我们预期的希望是结果在0-7中循环,不希望出现负数。那么C实现如何修改呢?

(-1 + 8) % 8 =>  7    // c

原理分析


计算机中的除法实现,**同号相除无问题,异号相除差异大,**要有所注意。

核心原因是异号除法各家定义不同,如java/c++/c,偏向使商最大,而python等新兴语言,则偏向使商最小。以下为除法取余的标准定义:

整除:a / d = q
取余:a % d = r
需满足:a = q * d + r, 且0 ≤ |r| < |d|。

在场景-1 % 8中,C语言实现: 商q为0,余数r为-1,满足自然数取模求余的定义。Python语言实现:商为-1,余数为7,也满足整数求余的定义,但Python能保证余数始终为0-7的正数。就C和Python语言的商而言,因为-1小于0,所以C语言的商更大。

若要使C语言也满足Python的类似输出,可以修改代码a % d为:(a + d) % d, 即可保证取余无负数。

进一步的,C语言中调整还有两种方案:

  • 粗调:(d - 1 + 8) % 8
  • 细调:(d + 1) % 8

粗调之所以加8,是为了保证d=0时,取余结果为7而非-1,保证余数都为正,且不断循环。d-1会导致结果可能为负,产生异号除法问题;而细调中,d+1则不会有这种问题,d=0时取余为1,向后偏移。所以,简化来看,前后两个区别只在于±1。

参考资料


  1. 实数范围内的求模(求余)运算:负数求余究竟怎么求, link

你可能感兴趣的:(计算机基础,python,c语言)