Clean, efficient algorithm for wrapping integers in C++

后端 未结 14 2057
慢半拍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: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        // 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 
    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 
    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);
    }
    

提交回复
热议问题