/**
* Returns a number between kLowerBound and kUpperBound
* e.g.: Wrap(-1, 0, 4); // Returns 4
* e.g.: Wrap(5, 0, 4); // Returns 0
*/
int Wrap(int const kX, int const kLowerBound, int const kUpperBound)
{
// Suggest an implementation?
}
答案 0 :(得分:30)
a % b
的符号仅在a
和b
均为非负数时定义。
int Wrap(int kX, int const kLowerBound, int const kUpperBound)
{
int range_size = kUpperBound - kLowerBound + 1;
if (kX < kLowerBound)
kX += range_size * ((kLowerBound - kX) / range_size + 1);
return kLowerBound + (kX - kLowerBound) % range_size;
}
答案 1 :(得分:17)
以下内容应独立于mod运算符的实现:
int range = kUpperBound - kLowerBound + 1;
kx = ((kx-kLowerBound) % range);
if (kx<0)
return kUpperBound + 1 + kx;
else
return kLowerBound + kx;
优于其他解决方案的优点是,它只使用一个%(即除法),这使它非常有效。
注意(关闭主题):
这是一个很好的例子,为什么有时候定义区间是明智的,上限是不在范围内的第一个元素(例如对于STL迭代器......)。在这种情况下,“+1”都会消失。
答案 2 :(得分:4)
最快的解决方案,最不灵活:利用将在硬件中打包的本机数据类型。
包装整数的绝对最快的方法将确保您的数据缩放到int8 / int16 / int32或任何本机数据类型。然后,当您需要数据包装时,本机数据类型将在硬件中完成!与此处所见的任何软件包装实现相比,非常无痛且数量级更快。
作为案例研究的一个例子:
我发现当我需要使用查找表实现sin / cos实现的sin / cos的快速实现时,这非常有用。基本上,您可以扩展数据,使INT16_MAX为pi,INT16_MIN为-pi。那你准备好了。
作为旁注,缩放数据会增加一些前期有限计算成本,通常看起来像:
int fixedPoint = (int)( floatingPoint * SCALING_FACTOR + 0.5 )
随意交换int以获得您想要的其他内容,例如int8_t / int16_t / int32_t。
下一个最快的解决方案,更灵活:如果可能的话,mod操作很慢,尝试使用位掩码!
我浏览的大多数解决方案在功能上都是正确的...但它们依赖于mod操作。
mod操作非常慢,因为它基本上是hardware division。对于为什么mod和除法缓慢的外行解释是将除法运算等同于一些伪代码for(quotient = 0;inputNum> 0;inputNum -= divisor) { quotient++; }
(def quotient and divisor)。正如你所看到的,硬件除法可以快速如果它相对于除数是一个较小的数字......但是如果它比除数大得多,那么除非非常慢。 /强>
如果您可以将数据扩展到2的幂,那么您可以使用将在一个周期内执行的位掩码(在99%的所有平台上执行)和您的速度提升将大约一个数量级(至少快2或3倍。
实现包装的C代码:
#define BIT_MASK (0xFFFF)
int wrappedAddition(int a, int b) {
return ( a + b ) & BIT_MASK;
}
int wrappedSubtraction(int a, int b) {
return ( a - b ) & BIT_MASK;
}
随意制作#define运行时的东西。并随意调整位掩码,使其成为您需要的两种功能。像0xFFFFFFFF或2的幂,你决定实施。
P.S。我强烈建议在弄乱包装/溢出条件时阅读有关定点处理的内容。我建议阅读:
Fixed-Point Arithmetic: An Introduction by Randy Yates August 23, 2007
答案 3 :(得分:2)
请不要忽视这篇文章。 :)
这有什么好处吗?
int Wrap(N,L,H){
H=H-L+1; return (N-L+(N<L)*H)%H+L;
}
这适用于负输入,只要L小于H,所有参数都可以为负。
背景...(请注意,此处的H
是重复使用的变量,设置为原始H-L+1
)。
我在递增时一直在使用(N-L)%H+L
,但与Lua不同,我在几个月前开始学习C之前使用过,如果我使用低于下限的输入,那就不行了,不要介意负输入。 (Lua是用C语言构建的,但我不知道它在做什么,它可能不会很快......)
我决定添加+(N<L)*H
来制作(N-L+(N<L)*H)%H+L
,因为C似乎被定义为true = 1和false = 0。它对我来说效果很好,似乎整齐地回答了原来的问题。如果有人知道如何在没有MOD运算符%的情况下做到令人眼花缭乱,请执行此操作。我现在不需要速度,但有一段时间我会,毫无疑问。
编辑:
如果N
低于L
的时间超过H-L+1
,则该功能失败,但事实并非如此:
int Wrap(N,L,H){
H-=L; return (N-L+(N<L)*((L-N)/H+1)*++H)%H+L;
}
我认为它会在任何系统中的整数范围的负极端处突破,但应该适用于大多数实际情况。它增加了额外的乘法和除法,但仍然相当紧凑。
(这个编辑只是为了完成,因为我想出了一个更好的方法,在这个帖子的新帖子中。)
乌鸦。
答案 4 :(得分:1)
实际上,由于-1%4在我所有的系统上都返回-1,因此简单的mod解决方案不起作用。我会尝试:
int range = kUpperBound - kLowerBound +1;
kx = ((kx - kLowerBound) % range) + range;
return (kx % range) + kLowerBound;
如果kx是正数,则修改,添加范围和修改,撤消添加。如果kx是负数,你修改,添加使其为正的范围,然后再次修改mod,这不会做任何事情。
答案 5 :(得分:1)
我个人认为,如果范围是排他性的,除数被限制为正值,那么这些类型的函数的解决方案就更清晰了。
int ifloordiv(int x, int y)
{
if (x > 0)
return x / y;
if (x < 0)
return (x + 1) / y - 1;
return 0
}
int iwrap(int x, int y)
{ return x - y * ifloordiv(x, y);
}
集成。
int iwrap(int x, int y)
{
if (x > 0)
return x % y;
if (x < 0)
return (x + 1) % y + y - 1;
return 0;
}
同一家人。为什么不呢?
int ireflect(int x, int y)
{
int z = iwrap(x, y*2);
if (z < y)
return z;
return y*2-1 - z;
}
int ibandy(int x, int y)
{
if (y != 1)
return ireflect(abs(x + x / (y - 1)), y);
return 0;
}
可以使用
为所有功能实现远程功能// output is in the range [min, max).
int func2(int x, int min, int max)
{
// increment max for inclusive behavior.
assert(min < max);
return func(x - min, max - min) + min;
}
答案 6 :(得分:0)
我会建议这个解决方案:
int Wrap(int const kX, int const kLowerBound, int const kUpperBound)
{
int d = kUpperBound - kLowerBound + 1;
return kLowerBound + (kX >= 0 ? kX % d : -kX % d ? d - (-kX % d) : 0);
}
?:
运算符的if-then-else逻辑确保%
的两个操作数都是非负的。
答案 7 :(得分:0)
一个具有一定对称性的答案,并且很明显当kX在范围内时,它会不加修改地返回。
int Wrap(int const kX, int const kLowerBound, int const kUpperBound)
{
int range_size = kUpperBound - kLowerBound + 1;
if (kX < kLowerBound)
return kX + range_size * ((kLowerBound - kX) / range_size + 1);
if (kX > kUpperBound)
return kX - range_size * ((kX - kUpperBound) / range_size + 1);
return kX;
}
答案 8 :(得分:0)
我会给出最常见情况的入口点lowerBound = 0,upperBound = N-1。并在一般情况下调用此函数。我已经在范围内没有进行mod计算。它假设上限> =更低,或者n> 0。
int wrapN(int i,int n)
{
if (i<0) return (n-1)-(-1-i)%n; // -1-i is >=0
if (i>=n) return i%n;
return i; // In range, no mod
}
int wrapLU(int i,int lower,int upper)
{
return lower+wrapN(i-lower,1+upper-lower);
}
答案 9 :(得分:0)
我也遇到过这个问题。这是我的解决方案。
template <> int mod(const int &x, const int &y) {
return x % y;
}
template <class T> T mod(const T &x, const T &y) {
return ::fmod((T)x, (T)y);
}
template <class T> T wrap(const T &x, const T &max, const T &min = 0) {
if(max < min)
return x;
if(x > max)
return min + mod(x - min, max - min + 1);
if(x < min)
return max - mod(min - x, max - min + 1);
return x;
}
我不知道它是不是很好,但我想我会分享,因为我在这里搜索这个问题并发现上述解决方案缺乏我的需求。 =)
答案 10 :(得分:0)
我的另一篇文章变得讨厌,所有'纠正'的乘法和除法都失控了。在看了Martin Stettner的帖子后,在我自己的(N-L)%H+L
的起始条件下,我想出了这个:
int Wrap(N,L,H){
H=H-L+1; N=(N-L)%H+L; if(N<L)N+=H; return N;
}
在整数范围的极端负端,它会像我的另一个那样断开,但它会更快,并且更容易阅读,并避免悄悄进入其中的其他肮脏。
乌鸦。
答案 11 :(得分:0)
在下限为零的特殊情况下,此代码避免了除法,模数和乘法。上限不必为2的幂。这段代码过于冗长,看上去很ated肿,但是编译为3条指令:减,移位(按常数)和“与”。
#include <climits> // CHAR_BIT
// -------------------------------------------------------------- allBits
// sign extend a signed integer into an unsigned mask:
// return all zero bits (+0) if arg is positive,
// or all one bits (-0) for negative arg
template <typename SNum>
static inline auto allBits (SNum arg) {
static constexpr auto argBits = CHAR_BIT * sizeof( arg);
static_assert( argBits < 256, "allBits() sign extension may fail");
static_assert( std::is_signed< SNum>::value, "SNum must be signed");
typedef typename std::make_unsigned< SNum>::type UNum;
// signed shift required, but need unsigned result
const UNum mask = UNum( arg >> (argBits - 1));
return mask;
}
// -------------------------------------------------------------- boolWrap
// wrap reset a counter without conditionals:
// return arg >= limit? 0 : arg
template <typename UNum>
static inline auto boolWrap (const UNum arg, const UNum limit) {
static_assert( ! std::is_signed< UNum>::value, "UNum assumed unsigned");
typedef typename std::make_signed< UNum>::type SNum;
const SNum negX = SNum( arg) - SNum( limit);
const auto signX = allBits( negX); // +0 or -0
return arg & signX;
}
// example usage:
for (int j= 0; j < 15; ++j) {
cout << j << boolWrap( j, 11);
}
答案 12 :(得分:-1)
对于负kX,您可以添加:
int temp = kUpperBound - kLowerBound + 1;
while (kX < 0) kX += temp;
return kX%temp + kLowerBound;
答案 13 :(得分:-2)
为什么不使用扩展方法。
public static class IntExtensions
{
public static int Wrap(this int kX, int kLowerBound, int kUpperBound)
{
int range_size = kUpperBound - kLowerBound + 1;
if (kX < kLowerBound)
kX += range_size * ((kLowerBound - kX) / range_size + 1);
return kLowerBound + (kX - kLowerBound) % range_size;
}
}
用法:currentInt = (++currentInt).Wrap(0, 2);