☰
-7 / 2 在 C 里是 -3,在 Python 里是 -4,谁算错了?
2026/10/3 16:32:58 网站建设 项目流程

参考:Andrew Koenig《C 陷阱与缺陷(第二版)》7.7节

目录

三条性质

三条不可能同时成立

C 到底保证了什么

例子:哈希表下标

实测:C 给的是哪一套

同一个式子,Python 给的是相反的答案


Koenig 在 7.7 节一上来没写实现,先问了一个问题:

「假定我们让 a 除以 b,商为 q,余数为 r。……我们希望 a、b、q、r 之间维持怎样的关系呢?」

然后列了三条。


三条性质

q*b + r == a——「最重要的一点……因为这是定义余数的关系」

这条好理解,这就是关于余数的反运算

改变 a 的正负号,q 的符号跟着变,绝对值不变

这是什么意思呢?比如:

q = 5 / 2 = 2;

r = 5 % 2 = 1;

此时改变被除数的符号: 5 -> -5 就有:

q = -5 / 2 = -2;

r = -5 % 2 = -1;

此时也满足第一条:

(-2)*2 + (-1) = -5;

但这样就会与第三条冲突:

b>0 时,保证r>=0且r<b——「如果余数用于哈希表的索引,确保它是一个有效的索引值很重要」

还是以 -5 举例,第三条应该这么运算:

q = -5 / 2 = -3;

r = -5 % 2 = 1;

这样也满足第一条:

(-3)*2 + 1 = -5;

但这样算与第二条就产生冲突了

第 3 条不是凑数的。做哈希表下标,余数必须是正数——这一点后面会用上。


三条不可能同时成立

书用3/2推导的:

  • 3/2:商 1,余 1 —— 性质 1 满足,没问题。
  • (-3)/2呢?
    • 先满足性质 2 → 商-1,那余数必须是-1→ 性质 3 破了(余数是负的)。
    • 先满足性质 3 → 余数1→ 由性质 1 反推商是-2→ 性质 2 破了。

两条路,必破一条。

书里的话:

因此,C 语言或者其他语言在实现整数除法截断运算时,必须放弃上述三条原则中的至少一条。大多数程序设计语言选择了放弃第 3 条,而改为要求余数与被除数的正负号相同。

注意这句:「大多数程序设计语言选择了放弃第 3 条」。有 "大多数",就说明有例外。


C 到底保证了什么

然而,C 语言的定义只保证了性质 1,以及当a>=0且b>0时,保证|r|<b以及r>=0。

后面部分的保证与性质 2 或者性质 3 比较起来,限制性要弱得多。

性质 1 是标准钉死的;性质 2 和性质 3 是"编译器实际上这么干",不是"标准要求这么干"。

(补一条后来发生的事:C99(1999 年)起,标准明文规定整数除法向零截断——"代数商去掉小数部分"。性质 2 从此由标准兜底。但性质 3 到今天也没人保证。)


例子:哈希表下标

书设了一个场景:n是标识符算出来的结果,要通过除法得到哈希表项h,满足0<=h<HASHSIZE。

如果已知n恒为非负,一行就够:

h = n % HASHSIZE;

如果n可能为负:

h = n % HASHSIZE; if (h < 0) h += HASHSIZE;

书最后还给了一条更省事的建议:「更好的做法是,程序在设计时就应避免 n 的值为负这样的情形,并且声明 n 为无符号数。」

到这里书讲完了。总结就是:C 放弃了"余数永远为正"这条,改用"余数跟被除数同号"。


实测:C 给的是哪一套

#include <stdio.h> int main(void) { printf("-7 / 2 = %d\n", -7 / 2); printf("-7 %% 2 = %d\n", -7 % 2); printf(" 7 / -2 = %d\n", 7 / -2); printf(" 7 %% -2 = %d\n", 7 % -2); printf("-7 / -2 = %d\n", -7 / -2); printf("-7 %% -2 = %d\n", -7 % -2); return 0; }
-7 / 2 = -3 -7 % 2 = -1 7 / -2 = -3 7 % -2 = 1 -7 / -2 = 3 -7 % -2 = -1

两条规律,跟书对得上:

商向零截断:

-7/2给-3,不是-4。这就是性质 2——符号变了,绝对值没变。

余数跟被除数同号

:-7 % 2 = -1(跟 -7 的负号)、7 % -2 = 1(跟 7 的正号)。这就是书说的"大多数 C 编译器在实践中的做法"。


同一个式子,Python 给的是相反的答案

把同样几组数丢给 Python:

(Python 的/是浮点除,所以对照要用整除//)

算式

C 的/

Python 的//

C 的%

Python 的%

-7 ÷ 2

-3

-4

-1

1

7 ÷ -2

-3

-4

1

-1

-7 ÷ -2

3

3

-1

-1

前两行全部不一样,而且余数的符号正好相反。

规律也清楚了:

  • C:余数的符号跟被除数
  • Python:余数的符号跟除数(7 % -2给-1,跟的是 -2)

回到第一章那三条性质——C 挑的是性质 2,Python 挑的是性质 3。

书说"大多数程序设计语言选择了放弃第 3 条",Python 就是那个没放弃的。

所以谁算错了?都没错。

标准没规定的地方,两种选择都合法。

书那句"大多数 C 编译器在实践中也都是这样做的"——"实践中"三个字,就是在说标准不管这事。


需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询