PS:笔者仅仅是个普通的大一菜鸡,文章中代码或者说明若有冗长不严谨之处,还请各位多多见谅,在评论区予以纠正
起因
前几天在刷OJ时,我写了这个函数
int
其中
是一个整型全局变量,它代表着一个整型全局数组
的长度,写代码的时候最初希望这个取模函数可以有效防止数组下标越界,并且完成一些特殊功能。在我接下来的代码中,有一个地方这样调用了这个函数
g_aCity[Mod1(g_aCapital[i] - 2)]
其中
是一个整型全局数组,用来储存一些特殊的下标,结果在
时,出现以下错误
此时
我:
探究
原本希望的返回值:
-1 % 34 = 33
实际返回值
-1 % 34 = -1
又在Python上试了一下
>>>
这才是我要的返回值!!!
一波科学上网,在wiki上找到如下数学定义
In mathematics, the result of the modulo operation is an equivalence class, and any member of the class may be chosen as representative; however, the usual representative is the least positive residue, the smallest non-negative integer that belongs to that class, i.e.the remainder of the Euclidean division.
翻译成人话就是,对于给定的整数
,应有一系列整数
满足
在数论中,习惯上取这个集合中的最小非负整数
作为模运算的值。这是数学定义,那么具体的编程语言是如何操作的呢?wiki上给了这个解释
However, other conventions are possible. Computers and calculators have various ways of storing and representing numbers; thus their definition of the modulo operation depends on the programming language or the underlying hardware.
可见模运算的结果取决于具体的语言,甚至有时候还取决于具体实现。那么在C上是如何操作的呢?
又写了几段测试代码
#include
输出结果
1
3
-1
-2
-2
-1
???好像又有几个与我们的预期不同,不清楚那就用模运算的基本方法硬刚!!
对于整数
,按照模运算的基本方法,应该先求整除结果再计算余数,即
现在问题来了,如果进行模运算的两个数异号,那么这个整除可能得到一个负数,比如例子中的
,精确值应为
,那么是取
还是
呢?根据输出的结果,可以判断应是取了
,对其他异号数据分析,都是向较大数取整,结合正整数的情况,我们可以说取整是向
的方向进行,查询得知,这种运算严格应该叫做
取余(
),
取模运算(
)过程与取余几乎一致,就是在取整的时候,取余是向
的方向取整,取模是向
方向取整,这导致了对于异号运算两者结果不同。
总结&补充
经过上述分析与求证我们得知,C语言中的%运算符进行的应该是取余运算,而不是取模运算,这就导致了那个函数实际上并不能保证下标不越界,考虑在循环中出现负下标一般是
等绝对值较小的负数,可以修改如下
int
即可实现我的要求。
另外,按照上述规则定义的取模运算,其结果也并不总是正的,例如,在Python脚本中,%进行取模运算,那么
>>>
那么有没有完全符合我们通常使用的取模运算的编程语言呢?答案是:有,比如Pascal语言(ISO - 7185和-10206)中的
,和
Maple里的
,具体每个编程语言的情况,可以参考以下链接(可能需要梯子)
https://en.wikipedia.org/wiki/Modulo_operationen.wikipedia.org