C ++ 'da dairesel kaydırma (döndürme) işlemleri için en iyi uygulamalar


96

Sol ve sağ kaydırma operatörleri (<< ve >>) C ++ 'da zaten mevcuttur. Ancak dairesel kaydırma veya döndürme işlemlerini nasıl yapacağımı bulamadım.

"Sola Döndür" ve "Sağa Döndür" gibi işlemler nasıl yapılabilir?

Burada iki kez sağa döndürülüyor

Initial --> 1000 0011 0100 0010

şunlarla sonuçlanmalıdır:

Final   --> 1010 0000 1101 0000

Bir örnek yardımcı olacaktır.

(Editörün notu: Döndürme sayısı sıfırsa veya tek bir döndürme makine talimatından daha fazlası için derlenirse, C'de döndürmeleri ifade etmenin birçok yaygın yolu tanımlanmamış davranıştan muzdariptir. Bu sorunun cevabı en iyi uygulamaları belgelemelidir.)



Yanıtlar:


106

Asm gcc / clang'ın x86 için ne ürettiği hakkında daha fazla ayrıntı içeren başka bir döndürme sorusundaki bu cevabın önceki bir versiyonuna da bakın .

Tanımlanmamış Davranışlardan kaçınan bir döndürmeyi C ve C ++ 'da ifade etmenin en derleyici dostu yolu, John Regehr'in uygulaması gibi görünüyor . Türün genişliğine göre döndürmek için uyarladım (sabit genişlikli türler gibi uint32_t).

#include <stdint.h>   // for uint32_t
#include <limits.h>   // for CHAR_BIT
// #define NDEBUG
#include <assert.h>

static inline uint32_t rotl32 (uint32_t n, unsigned int c)
{
  const unsigned int mask = (CHAR_BIT*sizeof(n) - 1);  // assumes width is a power of 2.

  // assert ( (c<=mask) &&"rotate by type width or more");
  c &= mask;
  return (n<<c) | (n>>( (-c)&mask ));
}

static inline uint32_t rotr32 (uint32_t n, unsigned int c)
{
  const unsigned int mask = (CHAR_BIT*sizeof(n) - 1);

  // assert ( (c<=mask) &&"rotate by type width or more");
  c &= mask;
  return (n>>c) | (n<<( (-c)&mask ));
}

Herhangi bir işaretsiz tamsayı türü için çalışır, yalnızca uint32_tbaşka boyutlar için sürümler oluşturabilirsiniz.

static_assertÖrneğin, bazı 24-bit DSP'lerde veya 36-bit mainframe'lerde durum böyle olmayan (tip genişliğinin 2'nin kuvveti olduğu da dahil olmak üzere) çok sayıda güvenlik denetimi içeren bir C ++ 11 şablon sürümüne bakın .

Döndürme genişliğini açıkça içeren adlara sahip sarmalayıcılar için şablonu yalnızca arka uç olarak kullanmanızı öneririm. Tamsayı yükseltme kuralları rotl_template(u16 & 0x11UL, 7), bunun 16 değil (genişliğine bağlı olarak unsigned long) 32 veya 64 bit döndürme yapacağı anlamına gelir . Hatta uint16_t & uint16_tterfi edilir signed intplatformlarda haricinde C ++ 'ın tam sayı-tanıtım kurallarına göre intdaha geniştir uint16_t.


X86 üzerinde , bu sürüm tek için inlinesrol r32, cl (veya rol r32, imm8derleyici olduğunu bildiği için, onu grok derleyiciler ile) x86 döndür ve kaydırma komutları maske C kaynak yok aynı şekilde kayması-sayar.

Değişken sayılı vardiyalar için uint32_t xve x86'da bu UB'den kaçınan deyim için derleyici desteği unsigned int n:

  • clang: clang3.5'ten bu yana değişken sayımlı dönüşler için tanınır, bundan önce çoklu + veya insns kaydırır.
  • gcc: gcc4.9'dan beri değişken sayı dönüşümleri için tanınır , bundan önce birden çok + veya insns vardiya. gcc5 ve daha sonra, wikipedia sürümündeki dalı ve maskeyi, değişken sayılar için yalnızca bir rorveya rolkomutunu kullanarak optimize eder .
  • icc: ICC13 veya daha önceki sürümlerden bu yana değişken sayımlı dönüşümler için desteklenir . Sabit-sayma , bir MOV kaydetmek için BMI2 mevcut olmadığında, bazı CPU'lardan (özellikle AMD, ancak bazı Intel) shld edi,edi,7daha yavaş ve daha fazla bayt alan kullanımı döndürür .rol edi,7rorx eax,edi,25
  • MSVC: x86-64 CL19: Yalnızca sabit sayı dönüşümleri için tanınır. (Wikipedia deyimi tanınır, ancak dal ve AND optimize edilmemiştir). X86'daki _rotl/ _rotrintrinsics'i kullanın <intrin.h>(x86-64 dahil).

ARM için gcc bir kullanır and r1, r1, #31değişken sayımı döndükçe için, ama yine de tek bir komutla gerçek döndürme yapar : ror r0, r0, r1. Dolayısıyla gcc, rotate-count'ların doğası gereği modüler olduğunun farkında değildir. ARM belgelerinin dediği gibi, "vardiya uzunluğuna sahip ROR n, 32'den fazlası vardiya uzunluğundaki ROR ile aynıdır n-32" . Sanırım burada gcc'nin kafası karışıyor çünkü ARM'deki sola / sağa kaymalar sayımı doyuruyor, bu nedenle 32 veya daha fazla bir kayma kaydı temizleyecektir. (X86'nın aksine, kaydırmalar, sayıyı döndürmelerle aynı şekilde maskelemektedir). Döndürme deyimini tanımadan önce muhtemelen bir AND komutuna ihtiyacı olduğuna karar verir, çünkü bu hedefte dairesel olmayan kaydırmalar nasıl çalışır.

Mevcut x86 derleyicileri, muhtemelen AND on ARM'den kaçınmadıklarından dolayı, 8 ve 16-bit dönüşler için değişken bir sayımı maskelemek için hala ekstra bir talimat kullanıyorlar. Bu, gözden kaçan bir optimizasyondur, çünkü performans herhangi bir x86-64 CPU'daki döndürme sayısına bağlı değildir. (Sayımların maskelenmesi, modern CPU'lar gibi sabit gecikmeyle değil, vardiyaları yinelemeli olarak ele aldığı için performans nedenleriyle 286 ile tanıtıldı.)

BTW, derleyicinin 32-nyalnızca sağa döndürme sağlayan ARM ve MIPS gibi mimarilerde bir sola döndürme uygulamasını yapmaktan kaçınmak için değişken sayımlı döndürmeler için sağa döndürmeyi tercih eder . (Bu, derleme zamanı sabiti sayımlarıyla optimize eder.)

Eğlenceli bir gerçek: ARM gerçekten adanmış bir kayma / döndürme talimatı yoktur, bu sadece MOV var kaynağına işlenen ROR modunda namlu-değiştiren geçiyor : mov r0, r0, ror r1. Böylece bir döndürme, bir EOR komutu veya başka bir şey için bir yazmaç-kaynak işlenene katlanabilir.


İçin işaretsiz türleri nve dönüş değerini kullandığınızdan emin olun , aksi takdirde bu bir döndürme olmaz . (x86 hedefleri için gcc aritmetik sağa kaydırma yapar, sıfır yerine işaret bitinin kopyalarını kaydırır ORve iki kaydırılmış değeri birlikte yaptığınızda bir soruna yol açar . Negatif işaretli tamsayıların sağa kaydırmaları, C'de uygulama tanımlı davranıştır.

Ayrıca, vardiya sayısının işaretsiz bir tür olduğundan emin olun , çünkü (-n)&31işaretli bir tür kişinin tümleyicisi veya işareti / büyüklüğü olabilir ve işaretsiz veya ikinin tümleyicisi ile elde ettiğiniz modüler 2 ^ n ile aynı olmayabilir. (Regehr'in blog gönderisindeki yorumlara bakın). unsigned intbaktığım her derleyicide, her genişliği için iyi sonuç veriyor x. Diğer bazı türler aslında bazı derleyiciler için deyim tanımayı bozar, bu nedenle ile aynı türü kullanmayın x.


Bazı derleyiciler döndürmeler için içsel bilgiler sağlar ; bu, taşınabilir sürüm hedeflediğiniz derleyicide iyi kod oluşturmazsa satır içi asm'den çok daha iyidir. Bildiğim herhangi bir derleyici için platformlar arası iç bilgi yok. X86 seçeneklerinden bazıları şunlardır:

  • Sağ vardiyayı <immintrin.h>sağlayan _rotlve _rotl64içsel olan Intel belgeleri . MSVC <intrin.h>, gcc gerektirirken <x86intrin.h>. An #ifdefgcc ve icc ile ilgilenir, ancak clang bunları MSVC uyumluluk modu dışında-fms-extensions -fms-compatibility -fms-compatibility-version=17.00 hiçbir yerde sağlamıyor gibi görünmektedir . Ve onlar için yaydığı asm berbattır (ekstra maskeleme ve bir CMOV).
  • MSVC: _rotr8ve_rotr16 .
  • gcc ve icc (çınlama değil): <x86intrin.h>Ayrıca sağlar __rolb/ __rorb8 bit döndürme / sağ, sol için __rolw/ __rorw(16-bit), __rold/ __rord(32-bit), __rolq/ __rorq(64-bit, sadece 64-bit hedefler için tanımlanan). Dar döndürmeler için, uygulama __builtin_ia32_rolhiveya kullanır ...qi, ancak 32 ve 64 bit döndürmeler, shift / veya kullanılarak tanımlanır (UB'ye karşı koruma olmadan, çünkü içindeki kod ia32intrin.hyalnızca x86 için gcc üzerinde çalışmak zorundadır). GNU C'nin olduğu gibi herhangi bir çapraz platform __builtin_rotateişlevine sahip olmadığı görülmektedir __builtin_popcount(bu, hedef platformda tek bir talimat olmasa bile en uygun olana genişler). Çoğu zaman deyim tanımadan iyi bir kod alırsınız.

// For real use, probably use a rotate intrinsic for MSVC, or this idiom for other compilers.  This pattern of #ifdefs may be helpful
#if defined(__x86_64__) || defined(__i386__)

#ifdef _MSC_VER
#include <intrin.h>
#else
#include <x86intrin.h>  // Not just <immintrin.h> for compilers other than icc
#endif

uint32_t rotl32_x86_intrinsic(rotwidth_t x, unsigned n) {
  //return __builtin_ia32_rorhi(x, 7);  // 16-bit rotate, GNU C
  return _rotl(x, n);  // gcc, icc, msvc.  Intel-defined.
  //return __rold(x, n);  // gcc, icc.
  // can't find anything for clang
}
#endif

Muhtemelen bazı x86 olmayan derleyiciler de içsel özelliklere sahiptir, ancak bu topluluk-wiki yanıtını hepsini içerecek şekilde genişletmeyelim. (Belki bunu içsel konularla ilgili mevcut yanıtta yapın ).


(Bu cevabın eski sürümü, MSVC'ye özgü satır içi asm (yalnızca 32bit x86 kodu için çalışır) veya bir C sürümü için http://www.devx.com/tips/Tip/14043 önerdi . Yorumlar buna yanıt veriyor .)

Satır içi asm , girdileri depolanmaya / yeniden yüklenmeye zorladığı için özellikle MSVC tarzı birçok optimizasyonu yener . Dikkatlice yazılmış bir GNU C satır içi asm döndürme, sayımın derleme zamanı sabiti kaydırma sayıları için anında işlenen olmasına izin verir, ancak kaydırılacak değer aynı zamanda bir derleme zamanı sabiti ise, yine de tamamen optimize edemez. satır içi yaptıktan sonra. https://gcc.gnu.org/wiki/DontUseInlineAsm .


1
Meraklı, neden olmasın bits = CHAR_BIT * sizeof(n);ve c &= bits - 1;ve return ((n >> c) | (n << (bits - c)))hangisini kullanacağım?
mirabilos

@mirabilos: Sürüm bit = 32 ile UB sahiptir tarafından vardiyada, = 32 saymak bits - c= 32 - 0. (Bundan bir ping almadım çünkü sadece wiki'yi düzenledim, ilk etapta
Peter Cordes

@PeterCordes 0 < count < bits, dönüşü uygulayan hemen hemen tüm CPU'ların ve programlama dillerinin sabit bir gereksinimidir (bazen 0 ≤ count < bits, ancak tam bit miktarına göre geçiş yapmak, hemen hemen her zaman tanımlı değildir veya değeri temizlemek ve döndürmek yerine sıfıra yuvarlanır.)
mirabilos

@mirabilos: Doğru, ancak amacımız vardiya sayısını doğrudan tek bir asm komutuna besleyen, ancak olası herhangi bir vardiya sayımı için C düzeyinde UB'den kaçınan bir işlev yazmak. C'nin bir döndürme operatörü veya işlevi olmadığından, bu deyimin bileşen parçalarından herhangi birinde UB'den kaçınmak istiyoruz. Derleyicinin bir C kaymasına, derlediği hedefteki asm kaydırma talimatlarıyla aynı şekilde davranmasına güvenmemeyi tercih ederiz. (Ve BTW, ARM, sayıyı yazmacın alt baytından alarak, yazmaç genişliğinden daha fazla değişken sayımlı kaymalara sahip kaydı sıfırlar. Yanıttaki bağlantı.)
Peter Cordes

1
"Sadece taşınabilir parçacıkları kullan" diyecektim ama sonra kodu kontrol ettim ve (a) UB'yi çağırıyor gibi görünüyor sıfır kaydırma sayıları ve (b) yalnızca MSVC'de içsel bilgileri kullanıyor . Genel olarak, derleyici ve platforma özgü tüm hack'lerle işe yarayanlar için derlenebilir "referans kodu" olarak buna sahip olmak güzel bir fikir gibi görünüyor ...
BeeOnRope

33

C ++ olduğundan, bir satır içi işlev kullanın:

template <typename INT> 
INT rol(INT val) {
    return (val << 1) | (val >> (sizeof(INT)*CHAR_BIT-1));
}

C ++ 11 varyantı:

template <typename INT> 
constexpr INT rol(INT val) {
    static_assert(std::is_unsigned<INT>::value,
                  "Rotate Left only makes sense for unsigned types");
    return (val << 1) | (val >> (sizeof(INT)*CHAR_BIT-1));
}

5
Uyarı: INTİşaretli bir tam sayı ise ve işaret ayarlanmışsa bu kod bozulur ! Örneğin rol<std::int32_t>(1 << 31)hangisinin 1'e dönmesi gerektiğini test edin, ancak gerçekte olur -1(çünkü işaret korunur).
Güneydoğu'dan kimse uzaklaşmıyor

9
@Nobody: Ben zaten 5 yıl önce imzalı tam sayı türlerini kullanmamanız gerektiğini söylemiştim. Rotasyon, işaretli tam sayı türlerinde zaten bir anlam ifade etmiyor.
Malters

2
Bunun std::numeric_limits<INT>::digitsyerine kullanabilirsiniz CHAR_BIT * sizeof. İşaretsiz türlerin kullanılmayan dolgulara sahip olup olmadıklarını unutuyorum (örneğin, 32 bitte saklanan 24 bitlik tamsayılar), ancak öyleyse daha digitsiyi olur. Değişken sayımlı bir kayma için daha fazla kontrol içeren bir sürüm için gist.github.com/pabigot/7550454'e de bakın .
Peter Cordes

1
@PeterCordes: Onlar. Crays'in yaptığını düşünüyorum (üs alanının olacağı yerde dolgulu kayan nokta kayıtları kullandı).
MSalters

2
@ fake-name '> yani C ++ 11 sürümü, siz onu başka bir şeye değiştirmedikçe pencerelerde çalışmayacaktır ...' Evet, bunu linux olarak değiştirin. :)
Slava

21

Çoğu derleyicinin bunun için içsel bilgileri vardır. Örnek _rotr8, _rotr16 için Visual Studio


Vaov! kabul edilen cevaptan çok daha kolay. btw, DWORD (32 bit) için _rotr ve _rotl kullanın.
Gabe Halsmer

17

C ++ 20 std::rotl vestd::rotr

Geldi! http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0553r4.html ve <bit>başlığa eklemeniz gerekir .

cppreference , kullanımın şöyle olacağını söylüyor :

#include <bit>
#include <bitset>
#include <cstdint>
#include <iostream>

int main()
{
    std::uint8_t i = 0b00011101;
    std::cout << "i          = " << std::bitset<8>(i) << '\n';
    std::cout << "rotl(i,0)  = " << std::bitset<8>(std::rotl(i,0)) << '\n';
    std::cout << "rotl(i,1)  = " << std::bitset<8>(std::rotl(i,1)) << '\n';
    std::cout << "rotl(i,4)  = " << std::bitset<8>(std::rotl(i,4)) << '\n';
    std::cout << "rotl(i,9)  = " << std::bitset<8>(std::rotl(i,9)) << '\n';
    std::cout << "rotl(i,-1) = " << std::bitset<8>(std::rotl(i,-1)) << '\n';
}

çıktı vermek:

i          = 00011101
rotl(i,0)  = 00011101
rotl(i,1)  = 00111010
rotl(i,4)  = 11010001
rotl(i,9)  = 00111010
rotl(i,-1) = 10001110

GCC'ye destek geldiğinde bir deneyeceğim, GCC 9.1.0 ile g++-9 -std=c++2ahala desteklemiyor.

Teklif şöyle diyor:

Üstbilgi:

namespace std {
  // 25.5.5, rotating   
  template<class T>
    [[nodiscard]] constexpr T rotl(T x, int s) noexcept;
  template<class T>
    [[nodiscard]] constexpr T rotr(T x, int s) noexcept;

ve:

25.5.5 Döndürme [bitops.rot]

Aşağıdaki açıklamalarda N ifade etsin std::numeric_limits<T>::digits.

template<class T>
  [[nodiscard]] constexpr T rotl(T x, int s) noexcept;

Kısıtlamalar: T, işaretsiz bir tam sayı türüdür (3.9.1 [temel. Temel]).

R, s% N olsun.

İadeler: Eğer r 0 ise, x; r pozitif ise (x << r) | (x >> (N - r)); r negatifse rotr(x, -r),.

template<class T>
  [[nodiscard]] constexpr T rotr(T x, int s) noexcept;

Kısıtlamalar: T, işaretsiz bir tam sayı türüdür (3.9.1 [temel. Temel]). R, s% N olsun.

İadeler: Eğer r 0 ise, x; r pozitif ise (x >> r) | (x << (N - r)); r negatifse rotl(x, -r),.

std::popcountAyrıca 1 bit sayısını saymak için A eklenmiştir: 32 bitlik bir tamsayıdaki set bitlerinin sayısı nasıl hesaplanır?


Bit rotasyonlarının modern c ++ 'ya inmesi neden bu kadar uzun sürdü? LLVM clang'da bile, sadece birkaç yıl önce içsel bilgiler vardı => reviews.llvm.org/D21457 ARM'in 2010'dan önce döndüğünü düşündüm, bu yüzden c ++ 11'den beri orada olmalıydılar.
sandthorn

15

Kesin olarak:

template<class T>
T ror(T x, unsigned int moves)
{
  return (x >> moves) | (x << sizeof(T)*8 - moves);
}

6
Bu 8bir yazım hatası mı CHAR_BIT(tam olarak 8 olması gerekmez)?
Toby Speight

2
Bu benimki ile aynı cevap olduğu için (sola doğru sağa değiştirmek dışında), Peter Cordes'in cevabım hakkındaki yorumu burada da geçerlidir: kullanın std::numeric_limits<T>::digits.
MSalters

7

Standart bit kümesini kullanarak böyle bir şey ne kadar abt ...

#include <bitset> 
#include <iostream> 

template <std::size_t N> 
inline void 
rotate(std::bitset<N>& b, unsigned m) 
{ 
   b = b << m | b >> (N-m); 
} 

int main() 
{ 
   std::bitset<8> b(15); 
   std::cout << b << '\n'; 
   rotate(b, 2); 
   std::cout << b << '\n'; 

   return 0;
}

HTH,


Bit kümesinin uzunluğundan daha büyük kaymaları hesaba katmak için onu değiştirmeniz gerekir.
H. Green

m %= N;Vardiya hesabına eklendi >= N.
Milania

7

X, 8 bitlik bir değer ise, bunu kullanabilirsiniz:

x=(x>>1 | x<<7);

2
xİmzalanmışsa muhtemelen yanlış davranacaktır .
sam hocevar

6

Detaylarda aşağıdaki mantığı uygulayabilirsiniz.

Bit Modeli Tam Sayıda 33602 ise

1000 0011 0100 0010

ve 2 sağ kaydırma ile Dönmeniz gerekir, sonra: önce bit modelinin bir kopyasını yapın ve sonra sola kaydırın: Uzunluk - Sağa Kaydırma yani uzunluk 16 sağa kaydırma değeri 2 16 - 2 = 14

14 kez vardiya kaldıktan sonra alırsınız.

1000 0000 0000 0000

Şimdi 33602 değerini 2 kez sağa kaydırın. Sen alırsın

0010 0000 1101 0000

Şimdi 14 kez sola kaydırılmış değer ile 2 kez sağa kaydırılmış değer arasında bir VEYA alın.

1000 0000 0000 0000
0010 0000 1101 0000
===================
1010 0000 1101 0000
===================

Ve kaydırılmış rollover değerinizi alırsınız. Biraz akıllıca işlemlerin daha hızlı olduğunu ve bunun herhangi bir döngü gerektirmediğini unutmayın.


1
Yukarıdaki alt rutinlere benzer ... b = b << m | b >> (Nm);
SM Kamran

Bunun OR değil, XOR olması gerekmez mi? 1 ^ 0 = 1, 0 ^ 0 = 0, vb. Eğer VEYA ise münhasır değilse, bu nedenle her zaman 1 olacaktır.
BK

5

Sağa Lbitlerle kaydırmak istediğinizi ve girişin bitli xbir sayı olduğunu varsayarsak N:

unsigned ror(unsigned x, int L, int N) 
{
    unsigned lsbs = x & ((1 << L) - 1);
    return (x >> L) | (lsbs << (N-L));
}

4

Doğru cevap şudur:

#define BitsCount( val ) ( sizeof( val ) * CHAR_BIT )
#define Shift( val, steps ) ( steps % BitsCount( val ) )
#define ROL( val, steps ) ( ( val << Shift( val, steps ) ) | ( val >> ( BitsCount( val ) - Shift( val, steps ) ) ) )
#define ROR( val, steps ) ( ( val >> Shift( val, steps ) ) | ( val << ( BitsCount( val ) - Shift( val, steps ) ) ) )

valİmzalanmışsa muhtemelen yanlış davranacaktır .
sam hocevar

0

Kaynak Kodu x bit numarası

int x =8;
data =15; //input
unsigned char tmp;
for(int i =0;i<x;i++)
{
printf("Data & 1    %d\n",data&1);
printf("Data Shifted value %d\n",data>>1^(data&1)<<(x-1));
tmp = data>>1|(data&1)<<(x-1);
data = tmp;  
}

0

başka bir öneri

template<class T>
inline T rotl(T x, unsigned char moves){
    unsigned char temp;
    __asm{
        mov temp, CL
        mov CL, moves
        rol x, CL
        mov CL, temp
    };
    return x;
}

0

Aşağıda, Dídac Pérez'in cevabının , her iki yönün de uygulandığı, işaretsiz karakter ve işaretsiz uzun uzun değerler kullanan bu işlevlerin kullanımlarının bir demosuyla birlikte biraz geliştirilmiş bir versiyonu bulunmaktadır . Birkaç not:

  1. İşlevler, derleyici optimizasyonları için sıralanmıştır
  2. cout << +valueBurada bulduğum işaretsiz bir karakteri sayısal olarak çıkarmak için bir numara kullandım : https://stackoverflow.com/a/28414758/1599699
  3. <put the type here>Açıklık ve güvenlik için açık sözdizimini kullanmanızı öneririm .
  4. Ben çünkü Ek Detaylar bölümünde bulunan ne shiftNum parametresi için unsigned char kullanılır burada :

Bir kaydırma işleminin sonucu, toplamsal ifade negatifse veya eklemeli ifade (yükseltilmiş) kaydırma ifadesindeki bit sayısından büyük veya ona eşitse tanımsızdır .

İşte kullandığım kod:

#include <iostream>

using namespace std;

template <typename T>
inline T rotateAndCarryLeft(T rotateMe, unsigned char shiftNum)
{
    static const unsigned char TBitCount = sizeof(T) * 8U;

    return (rotateMe << shiftNum) | (rotateMe >> (TBitCount - shiftNum));
}

template <typename T>
inline T rotateAndCarryRight(T rotateMe, unsigned char shiftNum)
{
    static const unsigned char TBitCount = sizeof(T) * 8U;

    return (rotateMe >> shiftNum) | (rotateMe << (TBitCount - shiftNum));
}

void main()
{
    //00010100 == (unsigned char)20U
    //00000101 == (unsigned char)5U == rotateAndCarryLeft(20U, 6U)
    //01010000 == (unsigned char)80U == rotateAndCarryRight(20U, 6U)

    cout << "unsigned char " << 20U << " rotated left by 6 bits == " << +rotateAndCarryLeft<unsigned char>(20U, 6U) << "\n";
    cout << "unsigned char " << 20U << " rotated right by 6 bits == " << +rotateAndCarryRight<unsigned char>(20U, 6U) << "\n";

    cout << "\n";


    for (unsigned char shiftNum = 0U; shiftNum <= sizeof(unsigned char) * 8U; ++shiftNum)
    {
        cout << "unsigned char " << 21U << " rotated left by " << +shiftNum << " bit(s) == " << +rotateAndCarryLeft<unsigned char>(21U, shiftNum) << "\n";
    }

    cout << "\n";

    for (unsigned char shiftNum = 0U; shiftNum <= sizeof(unsigned char) * 8U; ++shiftNum)
    {
        cout << "unsigned char " << 21U << " rotated right by " << +shiftNum << " bit(s) == " << +rotateAndCarryRight<unsigned char>(21U, shiftNum) << "\n";
    }


    cout << "\n";

    for (unsigned char shiftNum = 0U; shiftNum <= sizeof(unsigned long long) * 8U; ++shiftNum)
    {
        cout << "unsigned long long " << 3457347ULL << " rotated left by " << +shiftNum << " bit(s) == " << rotateAndCarryLeft<unsigned long long>(3457347ULL, shiftNum) << "\n";
    }

    cout << "\n";

    for (unsigned char shiftNum = 0U; shiftNum <= sizeof(unsigned long long) * 8U; ++shiftNum)
    {
        cout << "unsigned long long " << 3457347ULL << " rotated right by " << +shiftNum << " bit(s) == " << rotateAndCarryRight<unsigned long long>(3457347ULL, shiftNum) << "\n";
    }

    cout << "\n\n";
    system("pause");
}

0
--- Substituting RLC in 8051 C for speed --- Rotate left carry
Here is an example using RLC to update a serial 8 bit DAC msb first:
                               (r=DACVAL, P1.4= SDO, P1.5= SCLK)
MOV     A, r
?1:
MOV     B, #8
RLC     A
MOV     P1.4, C
CLR     P1.5
SETB    P1.5
DJNZ    B, ?1

Here is the code in 8051 C at its fastest:
sbit ACC_7  = ACC ^ 7 ; //define this at the top to access bit 7 of ACC
ACC     =   r;
B       =   8;  
do  {
P1_4    =   ACC_7;  // this assembles into mov c, acc.7  mov P1.4, c 
ACC     <<= 1;
P1_5    =   0;
P1_5    =   1;
B       --  ; 
    } while ( B!=0 );
The keil compiler will use DJNZ when a loop is written this way.
I am cheating here by using registers ACC and B in c code.
If you cannot cheat then substitute with:
P1_4    =   ( r & 128 ) ? 1 : 0 ;
r     <<=   1;
This only takes a few extra instructions.
Also, changing B for a local var char n is the same.
Keil does rotate ACC left by ADD A, ACC which is the same as multiply 2.
It only takes one extra opcode i think.
Keeping code entirely in C keeps things simpler sometimes.

-1

Bir işlevi aşırı yükleyin:

unsigned int rotate_right(unsigned int x)
{
 return (x>>1 | (x&1?0x80000000:0))
}

unsigned short rotate_right(unsigned short x) { /* etc. */ }

-1
#define ROTATE_RIGHT(x) ( (x>>1) | (x&1?0x8000:0) )

makro argümanı olarak ifadelerle kötü sürprizlerden kaçınmak için x'i parantez içine almalısınız.
Joey

3
Değer 16 bit değilse sessizce anlamsızlaşırsınız
James Hopkin

Bunu bir makro olarak tanımlıyorsanız, argüman olarak yan etkileri olan bir ifadeyi iletmekten kaçınmak için de dikkatli olunması gerekir.
Phil Miller
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.