Clean, efficient algorithm for wrapping integers in C++

后端 未结 14 2017
慢半拍i
慢半拍i 2020-12-13 09:21
/**
  * 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 con         


        
相关标签:
14条回答
  • 2020-12-13 09:52

    An answer that has some symmetry and also makes it obvious that when kX is in range, it is returned unmodified.

    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;
    }
    
    0 讨论(0)
  • 2020-12-13 09:54

    Personally I've found solutions to these types of functions to be cleaner if range is exclusive and divisor is restricted to positive values.

    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);
    }
    

    Integrated.

    int iwrap(int x, int y)
    {
        if (x > 0)
            return x % y;
        if (x < 0)
            return (x + 1) % y + y - 1;
        return 0;
    }
    

    Same family. Why not?

    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;
    }
    

    Ranged functionality can be implemented for all functions with,

    // 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;
    }
    
    0 讨论(0)
  • 2020-12-13 09:54

    Actually, since -1 % 4 returns -1 on every system I've even been on, the simple mod solution doesn't work. I would try:

    int range = kUpperBound  - kLowerBound +1;
    kx = ((kx - kLowerBound) % range) + range;
    return (kx % range) + kLowerBound;
    

    if kx is positive, you mod, add range, and mod back, undoing the add. If kx is negative, you mod, add range which makes it positive, then mod again, which doesn't do anything.

    0 讨论(0)
  • 2020-12-13 09:55

    I would give an entry point to the most common case lowerBound=0, upperBound=N-1. And call this function in the general case. No mod computation is done where I is already in range. It assumes upper>=lower, or 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);
    }
    
    0 讨论(0)
  • 2020-12-13 09:56

    In the special case where the lower bound is zero, this code avoids division, modulus and multiplication. The upper bound does not have to be a power of two. This code is overly verbose and looks bloated, but compiles into 3 instructions: subtract, shift (by constant), and 'and'.

    #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);
    }
    
    0 讨论(0)
  • 2020-12-13 09:59

    Why not using Extension methods.

    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;
        }
    }
    

    Usage: currentInt = (++currentInt).Wrap(0, 2);

    0 讨论(0)
提交回复
热议问题