簡體   English   中英

將 C++ 中的十六進制數向左旋轉

[英]Rotate left part of hex number in C++

我有以下問題:我在 C++ 中有一個十六進制數(數據類型:std::uint64_t),十六進制數包含從 1 到給定 n 的所有數字。 現在的問題是旋轉十六進制數的前 k 位,例如:

hex = 0x436512, k = 3 --> 0x634512

我已經嘗試將十六進制數分成兩部分,例如

std::uint64_t left = hex >> ((n - k) * 4);
std::uint64_t right = ((1UL << ((n - k) * 4)) - 1) & hex;

然后left旋轉並將left right在一起。 是否有可能就地執行此操作並且僅使用位操作和/或數學運算符?

作為基線,您可以使用它,它基本上轉換為數字並返回。

#include <cstdio>
#include <cstdint>
#include <array>

uint64_t rotate( uint64_t value, int k ) {
    // decompose
    std::array<uint8_t,16> digits;
    int numdigits = 0;
    while ( value > 0 ) {
        digits[numdigits] = value % 16;
        value = value / 16;
        numdigits += 1;
    }
    if ( k>numdigits ) return 0;
    // revert digits
    int p1 = numdigits - 1;
    int p2 = numdigits - k;
    for ( ; p1>p2; p1--,p2++ ) {
        uint8_t tmp = digits[p1];
        digits[p1] = digits[p2];
        digits[p2] = tmp;
    }
    // reconstruct
    for ( int j=0; j<numdigits; ++j ) {
        value = (value*16) + digits[numdigits-1-j];
    }
    return value;
}

int main() {
    uint64_t value = 0x123ffffff;
    for ( int  j=0; j<8; ++j ) {
        value = value >> 4;
        printf( "%lx %lx\n", value, rotate(value,3) );
    }
}

Godbolt: https://godbolt.org/z/77qWEo9vE

它產生:

Program stdout
123fffff 321fffff
123ffff 321ffff
123fff 321fff
123ff 321ff
123f 321f
123 321
12 0
1 0

你其實不需要分解整個數字,你可以嚴格只分解左邊的數字。

#include <cstdio>
#include <cstdint>
#include <array>

uint64_t rotate( uint64_t value, int k ) {
    // sanity check
    if ( value == 0 ) return 0;

    // fast find number of digits
    int numdigits = (63-__builtin_clzl(value))/4 + 1;
    if ( k>numdigits ) return 0;

    // Decompose left and right
    int rightbits = 4*(numdigits-k);
    uint64_t left = value >> rightbits;
    uint64_t right = rightbits==0 ? 0 : value & (uint64_t(-1)>>(64-rightbits));


    // decompose left
    uint64_t rot = 0;
    for ( int j=0; j<k; ++j ) {
        uint64_t digit = left % 16;
        left = left / 16;
        rot = (rot*16) + digit;
    }
  
    // rejoin
    return right | (rot<<rightbits);
}

int main() {
    uint64_t value = 0x123ffffff;
    for ( int  j=0; j<8; ++j ) {
        value = value >> 4;
        printf( "%lx %lx\n", value, rotate(value,3) );
    }
}

產生相同的 output。

神箭: https://godbolt.org/z/P3z6W8b3M

在谷歌基准測試下運行:

#include <benchmark/benchmark.h>
#include <vector>
#include <iostream>

uint64_t rotate1(uint64_t value, int k);
uint64_t rotate2(uint64_t value, int k);

struct RotateTrivial {
    uint64_t operator()(uint64_t value, int k) {
        return rotate1(value, k);
    }
};

struct RotateLeftOnly {
    uint64_t operator()(uint64_t value, int k) {
        return rotate2(value, k);
    }
};

template <typename Fn>
static void Benchmark(benchmark::State& state) {
    Fn fn;
    for (auto _ : state) {
        uint64_t value = uint64_t(-1);
        for (int j = 0; j < 16; ++j) {
            for (int k = 1; k < j; ++k) {
                uint64_t result = fn(value, k);
                benchmark::DoNotOptimize(result);
            }
            value = value >> 4;
        }
    }
}

BENCHMARK(Benchmark<RotateTrivial>);
BENCHMARK(Benchmark<RotateLeftOnly>);
BENCHMARK_MAIN();

在 AMD Threadripper 3960x 3.5GHz 上生成

--------------------------------------------------------------------
Benchmark                          Time             CPU   Iterations
--------------------------------------------------------------------
Benchmark<RotateTrivial>         619 ns          619 ns      1158174
Benchmark<RotateLeftOnly>        320 ns          320 ns      2222098

每次迭代有 105 次調用,因此普通版本約為 6.3 ns/調用或 20 個周期,優化版本約為 3.1ns/調用或 10 個周期。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM