mod与modulo是Fortran用于计算余数的元素内在函数,两者在参数同号时常得到相同结果,但采用不同的商取整规则,因此负数参与时可能不同。mod常用于需要与趋零整数除法一致的余数计算;modulo适合在周期边界、循环下标、角度归一化等需要把负值映射到固定区间的情况中使用。
语法结构
remainder = mod(a, p)
remainder = modulo(a, p)
- a是被除数,p是除数;两者必须同为整数或同为实数,并具有相同种类,且p不能为零。
- mod(a,p)按a-int(a/p)*p定义,商向零截断;非零结果的符号与a相同。
- modulo(a,p)按a-floor(a/p)*p定义,商向负无穷取整;非零结果的符号与p相同。
- 两种结果的绝对值都小于abs(p)。当p为正数时,modulo满足0≤结果<p;若参数为整数,结果就是0到p-1中的整数。
本文代码
! 比较MOD与MODULO的符号规则,并演示周期下标映射。
program modModuloDemo
implicit none
integer, parameter :: dividends(*) = [7, -7, 7, -7]
integer, parameter :: divisors(*) = [3, 3, -3, -3]
integer :: i
integer :: rawIndex, wrappedIndex
print "(A)", " a p mod(a,p) modulo(a,p)"
do i = 1, size(dividends)
print "(2I4,2I11)", dividends(i), divisors(i), &
mod(dividends(i), divisors(i)), modulo(dividends(i), divisors(i))
end do
rawIndex = -3
wrappedIndex = modulo(rawIndex, 8)
print "(/,A,I0,A,I0)", "周期下标:", rawIndex, " -> ", wrappedIndex
end program modModuloDemo
编译运行
(base) hong@hongdeMacBook-Pro 046.modModulo % gfortran exampleModModulo.f90
(base) hong@hongdeMacBook-Pro 046.modModulo % ./a.out
a p mod(a,p) modulo(a,p)
7 3 1 1
-7 3 -1 2
7 -3 1 -2
-7 -3 -1 -1
周期下标:-3 -> 5
现象分析
当a=7、p=3时,7除以3的截断商和向下取整商都是2,因此两个函数都计算7-2×3并得到1。参数同为负数时,-7除以-3的商为正数2,两种规则也再次得到相同余数-1。
当a=-7、p=3时,商为-2.333…。mod使用趋零商-2,计算-7-(-2)×3=-1;modulo使用向下取整商-3,计算-7-(-3)×3=2。两者相差一个除数,但分别满足自己的符号规则。
当除数改为-3时,mod(7,-3)仍跟随被除数得到1,而modulo(7,-3)跟随除数得到-2。由此可见,不能只用正除数理解两者差异。
周期长度为8时,合法下标为0到7。modulo(-3,8)得到5,使向左越过起点3格的下标回绕到区间内部;如果使用mod(-3,8)则仍得到-3,不能直接作为该周期区间的下标。
核心知识点
- mod基于趋零商,非零余数跟随被除数符号;modulo基于向下取整商,非零余数跟随除数符号。
- 两个参数为正数时结果相同不足以证明函数等价,比较时必须覆盖负被除数和负除数。
- 正周期长度下的区间回绕通常使用modulo,因为结果稳定落在非负周期区间。
- 除数不能为零;处理实数参数时还要考虑浮点舍入,不能把近似余数直接当作精确零。