对于由范围R = [x,y]限制的任何整数输入W,对于缺少更好的项,“溢出”,W对R的是W % (y-x+1) + x
。如果W超过y,这会导致它回绕。
作为这个原则的一个例子,假设我们迭代一个日历的月份:
int this_month = 5;
int next_month = (this_month + 1) % 12;
其中两个整数都在0到11之间(包括0和11)。因此,上面的表达式将整数“钳制”到范围R = [0,11]。这种使用表达式的方法简单,优雅且有利,因为它省略了分支。
现在,如果我们想要做同样的事情,但倒退呢?以下表达式有效:
int last_month = ((this_month - 1) % 12 + 12) % 12;
但这是深奥的。它怎么能被美化?
tl; dr - 可以进一步简化表达式((x-1) % k + k) % k
吗?
注意:指定C ++标记,因为其他语言以不同方式处理模运算符的负操作数。
你的表达应该是((x-1) + k) % k
。这将正确地将x = 0包裹到11左右。通常,如果要退回多于1,则需要确保添加足够的值以使模运算的第一个操作数> = 0。
这是C ++中的一个实现:
int wrapAround(int v, int delta, int minval, int maxval)
{
const int mod = maxval + 1 - minval;
if (delta >= 0) {return (v + delta - minval) % mod + minval;}
else {return ((v + delta) - delta * mod - minval) % mod + minval;}
}
这也允许使用标记为0到11或1到12的月份,相应地设置min_val
和max_val
。
由于这个答案非常受欢迎,这里是一个没有分支的改进版本,它也处理初始值v
小于minval
的情况。我保留另一个例子,因为它更容易理解:
int wrapAround(int v, int delta, int minval, int maxval)
{
const int mod = maxval + 1 - minval;
v += delta - minval;
v += (1 - v / mod) * mod;
return v % mod + minval;
}
剩下的唯一问题是如果minval
大于maxval
。如果需要,请随意添加断言。
k%k将始终为0.我不是100%确定你要做什么,但似乎你希望上个月被夹在0到11之间(包括0和11)。
(this_month + 11) % 12
应该就够了。
一般的解决方案是编写一个计算所需值的函数:
//Returns floor(a/n) (with the division done exactly).
//Let ÷ be mathematical division, and / be C++ division.
//We know
// a÷b = a/b + f (f is the remainder, not all
// divisions have exact Integral results)
//and
// (a/b)*b + a%b == a (from the standard).
//Together, these imply (through algebraic manipulation):
// sign(f) == sign(a%b)*sign(b)
//We want the remainder (f) to always be >=0 (by definition of flooredDivision),
//so when sign(f) < 0, we subtract 1 from a/n to make f > 0.
template<typename Integral>
Integral flooredDivision(Integral a, Integral n) {
Integral q(a/n);
if ((a%n < 0 && n > 0) || (a%n > 0 && n < 0)) --q;
return q;
}
//flooredModulo: Modulo function for use in the construction
//looping topologies. The result will always be between 0 and the
//denominator, and will loop in a natural fashion (rather than swapping
//the looping direction over the zero point (as in C++11),
//or being unspecified (as in earlier C++)).
//Returns x such that:
//
//Real a = Real(numerator)
//Real n = Real(denominator)
//Real r = a - n*floor(n/d)
//x = Integral(r)
template<typename Integral>
Integral flooredModulo(Integral a, Integral n) {
return a - n * flooredDivision(a, n);
}
Easy Peasy,不要使用第一个模块操作符,这是多余的:
int last_month = (this_month - 1 + 12) % 12;
这是一般情况
在这种情况下,你可以写11
,但我仍然会做-1 + 11
,因为它更清楚地说明你想要实现的目标。
不确定你是否和我有同样的问题,但我的问题基本上是我想将所有数字限制在一定的范围内。假设范围是0-6,因此使用%7意味着任何高于6的数字将回绕到0或更高。实际问题是小于零的数字没有回绕到6.我有一个解决方案(其中X是数字范围的上限,0是最小值):
if(inputNumber <0)//If this is a negative number
{
(X-(inputNumber*-1))%X;
}
else
{
inputNumber%X;
}