Membulatkan ke kekuatan berikutnya 2


189

Saya ingin menulis fungsi yang mengembalikan kekuatan terdekat 2 angka. Sebagai contoh jika input saya 789, output harus 1024. Apakah ada cara untuk mencapai ini tanpa menggunakan loop tetapi hanya menggunakan beberapa operator bitwise?



4
Dengan klarifikasi, apakah Anda memerlukan kekuatan terdekat 2 (mis. 65 akan memberi Anda 64, tetapi 100 akan memberi Anda 128) atau yang terdekat di atas (mis. 65 akan memberi Anda 128, dan demikian juga 100)?
Kim Reece

1
Ada banyak pertanyaan yang cocok dengan yang ini. Sebagai contoh: stackoverflow.com/questions/364985/…
Yann Droneaud


7
@Nathan Tautan Anda 8 bulan lebih lambat dari pertanyaan ini.
Joseph Quinsey

Jawaban:


148

Periksa Bit Twiddling Hacks . Anda perlu mendapatkan logaritma basis 2, lalu tambahkan 1 untuk itu. Contoh untuk nilai 32-bit:

Membulatkan ke kekuatan tertinggi berikutnya 2

unsigned int v; // compute the next highest power of 2 of 32-bit v

v--;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
v++;

Perpanjangan ke lebar lain harus jelas.


11
Ini bukan solusi yang paling efisien karena banyak prosesor memiliki instruksi khusus untuk menghitung nol terkemuka yang dapat digunakan untuk menghitung log2 dengan sangat efisien. Lihat en.wikipedia.org/wiki/Find_first_set
Simon

7
@Simon: ini solusi portabel. Tidak ada algoritma umum yang efisien untuk semua arsitektur
phuclv

5
Bagaimana jika angka itu sendiri adalah kekuatan dua?
Litherum

5
Utas ini masih dirujuk dengan baik tetapi jawaban ini (dan sebagian besar lainnya) sudah usang. CPU memiliki instruksi untuk membantu ini (sebenarnya sudah pada saat itu?). Dari: jameshfisher.com/2018/03/30/round-up-power-2.html uint64_t next_pow2(uint64_t x) { return x == 1 ? 1 : 1<<(64-__builtin_clzl(x-1)); } Dan untuk 32 bit: uint32_t next_pow2(uint32_t x) { return x == 1 ? 1 : 1<<(32-__builtin_clz(x-1)); }Itu jika Anda menggunakan GCC (dan menurut saya, Dentang?), Tetapi akan lebih bijaksana jika meluangkan waktu untuk temukan panggilan ke CLZ alih-alih menempelkan semua opsi di sekitar.
MappaM

2
@MappaM Jawaban ini masih sangat relevan dan cara portabel terbaik untuk melakukannya. Versi 64-bit Anda memiliki perilaku yang tidak terdefinisi jika x > UINT32_MAXdan tidak bercabang. Juga, GCC dan Dentang digunakan -mtune=genericsecara default (seperti kebanyakan distro), jadi kode Anda TIDAK akan meluas ke lzcntinstruksi pada x86_64 - itu sebenarnya akan meluas ke sesuatu yang JAUH lebih lambat (rutin libgcc) kecuali Anda menggunakan sesuatu seperti -march=native. Jadi pengganti yang Anda ajukan adalah non-portable, buggy dan (biasanya) lebih lambat.
Craig Barnes

76
next = pow(2, ceil(log(x)/log(2)));

Ini berfungsi dengan menemukan angka yang harus Anda naikkan 2 untuk mendapatkan x (ambil log nomor tersebut, dan bagi dengan log basis yang diinginkan, lihat wikipedia untuk informasi lebih lanjut ). Kemudian kumpulkan dengan langit-langit untuk mendapatkan kekuatan bilangan bulat terdekat.

Ini adalah metode yang lebih umum (yaitu lebih lambat!) Daripada metode bitwise yang ditautkan di tempat lain, tetapi bagus untuk mengetahui matematika, eh?


3
Dari C99, Anda juga bisa menggunakan log2 jika didukung oleh alat Anda. GCC dan VS tampaknya tidak :(
Matius Baca

2
Anda kehilangan braket ... selanjutnya = pow (2, ceil (log (x) / log (2)));
Matthieu Cormier

13
Berhati-hatilah dengan akurasi float. log(pow(2,29))/log(2)= 29.000000000000004, jadi hasilnya adalah 2 30 bukannya mengembalikan 2 29. Saya pikir ini adalah mengapa fungsi log2 ada?
endolith

48
Biaya ini mungkin setidaknya 200 siklus dan itu bahkan tidak benar. Mengapa ini memiliki begitu banyak upvotes?
Axel Gneiting

4
@ SupupflyJon Tapi itu menyebutkan operator bitwise dan saya menganggap kebenaran tersirat oleh pertanyaan apa pun kecuali disebutkan sebaliknya.
BlackJack

50
unsigned long upper_power_of_two(unsigned long v)
{
    v--;
    v |= v >> 1;
    v |= v >> 2;
    v |= v >> 4;
    v |= v >> 8;
    v |= v >> 16;
    v++;
    return v;

}

62
Akan lebih baik jika Anda menghubungkannya (kecuali Anda menemukannya). Itu berasal dari halaman hack twiddling sedikit.
florin

3
Apakah itu untuk nomor 32-bit? Ekstensi untuk 64-bit?
Jonathan Leffler

Jonathan, kamu harus melakukannya untuk bagian atas, dan jika itu nol, kamu melakukannya untuk bagian bawah.
florin

5
@ florin, jika v adalah tipe 64-bit, tidak bisakah Anda menambahkan "c | = v >> 32" setelah yang satu untuk 16?
Evan Teran

3
Kode yang hanya berfungsi untuk lebar bit tertentu harus menggunakan tipe lebar tetap dan bukan tipe lebar minimum. Fungsi ini harus mengambil dan mengembalikan a uint32_t.
Craig Barnes

50

Saya pikir ini juga berfungsi:

int power = 1;
while(power < x)
    power*=2;

Dan jawabannya adalah power.


19
Cukup adil, pertanyaannya tidak ada loop. Tetapi sepintar beberapa fungsi lainnya, untuk kode yang tidak peka terhadap kinerja jawaban yang cepat dan mudah dipahami dan diverifikasi menjadi benar selalu menang untuk saya.
Tim MB

2
Ini bukan mengembalikan kekuatan terdekat 2, tetapi kekuatan yang langsung lebih besar dari X. Masih sangat bagus
CoffeDeveloper

1
Alih-alih mengalikan, beberapa "sihir" bitwise dapat digunakan sebagai gantinyapower <<= 1
vallentin

5
@Vallentin Itu harus secara otomatis dioptimalkan oleh kompiler.
MarkWeston

4
Waspadai infinite loop jika xterlalu besar (mis. Tidak cukup bit untuk mewakili kekuatan 2 berikutnya).
alban

36

Jika Anda menggunakan GCC, Anda mungkin ingin melihat Mengoptimalkan fungsi next_pow2 () oleh Lockless Inc. .. Halaman ini menjelaskan cara untuk menggunakan fungsi bawaan builtin_clz()(menghitung memimpin nol) dan kemudian menggunakan langsung x86 (ia32) assembler instruksi bsr(bit pemindaian terbalik), seperti itu dijelaskan dalam jawaban lain 's link ke situs gamedev . Kode ini mungkin lebih cepat daripada yang dijelaskan dalam jawaban sebelumnya .

Omong-omong, jika Anda tidak akan menggunakan instruksi assembler dan tipe data 64bit, Anda bisa menggunakan ini

/**
 * return the smallest power of two value
 * greater than x
 *
 * Input range:  [2..2147483648]
 * Output range: [2..2147483648]
 *
 */
__attribute__ ((const))
static inline uint32_t p2(uint32_t x)
{
#if 0
    assert(x > 1);
    assert(x <= ((UINT32_MAX/2) + 1));
#endif

    return 1 << (32 - __builtin_clz (x - 1));
}

3
Perhatikan bahwa ini mengembalikan kekuatan terkecil 2 lebih besar dari OR sama dengan x. Mengubah (x -1) ke x mengubah fungsi untuk mengembalikan kekuatan 2 yang lebih kecil dari x.
Guillaume

2
Anda dapat menggunakan _BitScanForwardVisual C ++
KindDragon

Anda juga dapat menggunakan__builtin_ctz()
MarkP

@MarkP __builtin_ctz()tidak akan berguna untuk membulatkan non power dari 2 angka hingga kekuatan dua berikutnya
Yann Droneaud

2
Harap tambahkan jawaban Anda di tautan ke daftar Wikipedia tentang fungsi bitwise bawaan untuk kompiler lain: en.wikipedia.org/wiki/Find_first_set#Tool_and_library_support                                Harap sediakan juga versi 64-bit. Saya mengusulkan fungsi C ++ 11 berikut:              constexpr uint64_t nextPowerOfTwo64 (uint64_t x) { return 1ULL<<(sizeof(uint64_t) * 8 - __builtin_clzll(x)); }
olibre

15

Satu lagi, meskipun saya menggunakan siklus, tetapi ini jauh lebih cepat daripada operan matematika

kekuatan dua opsi "lantai":

int power = 1;
while (x >>= 1) power <<= 1;

kekuatan dua opsi "ceil":

int power = 2;
x--;    // <<-- UPDATED
while (x >>= 1) power <<= 1;

MEMPERBARUI

Seperti disebutkan dalam komentar ada kesalahan di ceilmana hasilnya salah.

Berikut adalah fungsi lengkapnya:

unsigned power_floor(unsigned x) {
    int power = 1;
    while (x >>= 1) power <<= 1;
    return power;
}

unsigned power_ceil(unsigned x) {
    if (x <= 1) return 1;
    int power = 2;
    x--;
    while (x >>= 1) power <<= 1;
    return power;
}

2
hasilnya tidak benar jika xkekuatan 2. Mikro untuk menguji apakah input adalah kekuatan 2 diperlukan. #define ISPOW2(x) ((x) > 0 && !((x) & (x-1)))
pgplus1628

@ zorksylar lebih efisien adalah untukif (x == 0) return 1; /* Or 0 (Which is what I use) */ x--; /* Rest of program */
yyny

Solusi bagus! tapi power of two "ceil" optionitu tidak benar. Misalnya, ketika x = 2hasilnya seharusnya 2bukan4
MZD

10

Untuk jenis yang tidak ditandatangani, membangun Bit Twiddling Hacks:

#include <climits>
#include <type_traits>

template <typename UnsignedType>
UnsignedType round_up_to_power_of_2(UnsignedType v) {
  static_assert(std::is_unsigned<UnsignedType>::value, "Only works for unsigned types");
  v--;
  for (size_t i = 1; i < sizeof(v) * CHAR_BIT; i *= 2) //Prefer size_t "Warning comparison between signed and unsigned integer"
  {
    v |= v >> i;
  }
  return ++v;
}

Sebenarnya tidak ada loop di sana sebagai kompiler tahu pada waktu kompilasi jumlah iterasi.


4
Perhatikan bahwa pertanyaannya adalah tentang C.
martinkunev

@martinkunev Cukup ganti UnsignedType dan proses secara manual. Saya cukup yakin seorang programmer C dapat memperluas template sederhana ini dengan mengabaikan std::is_unsigned<UnsignedType>::valuepernyataan tersebut.
user877329

2
@ user877329 Tentu, akan menyenangkan untuk memiliki jawaban dalam Javascript juga, kalau-kalau ada orang yang ingin menerjemahkannya ke C.
martinkunev

@martinkunev UnsignedType dalam JavaScript? Bagaimanapun, solusi ini menunjukkan cara melakukannya untuk UnsignedType apa pun, dan kebetulan ditulis dalam C ++, daripada pseudocode [sizeof (v) * CHAR_BIT alih-alih sesuatu seperti jumlah bit dalam objek UnsignedType].
user877329

9

Untuk pelampung IEEE Anda bisa melakukan hal seperti ini.

int next_power_of_two(float a_F){
    int f = *(int*)&a_F;
    int b = f << 9 != 0; // If we're a power of two this is 0, otherwise this is 1

    f >>= 23; // remove factional part of floating point number
    f -= 127; // subtract 127 (the bias) from the exponent

    // adds one to the exponent if were not a power of two, 
    // then raises our new exponent to the power of two again.
    return (1 << (f + b)); 
}

Jika Anda membutuhkan solusi integer dan Anda dapat menggunakan perakitan inline, BSR akan memberi Anda log2 integer pada x86. Itu menghitung berapa banyak bit kanan diatur, yang persis sama dengan log2 dari angka itu. Prosesor lain memiliki instruksi yang serupa (sering), seperti CLZ dan tergantung pada kompiler Anda, mungkin ada intrinsik yang tersedia untuk melakukan pekerjaan untuk Anda.


Ini adalah salah satu yang menarik walaupun tidak terkait dengan pertanyaan (saya ingin melengkapi hanya bilangan bulat), akan mencoba yang satu ini ..
Naveen

Datang dengan itu setelah membaca artikel wikipedia tentang pelampung. Selain itu, saya sudah menggunakannya untuk menghitung akar kuadrat dalam presisi integer. Juga bagus, tetapi bahkan lebih tidak berhubungan.
Jasper Bekkers

Ini melanggar aturan aliasing yang ketat. Pada beberapa kompiler, ini mungkin tidak berfungsi atau mengeluarkan peringatan.
martinkunev


5
/*
** http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog
*/
#define __LOG2A(s) ((s &0xffffffff00000000) ? (32 +__LOG2B(s >>32)): (__LOG2B(s)))
#define __LOG2B(s) ((s &0xffff0000)         ? (16 +__LOG2C(s >>16)): (__LOG2C(s)))
#define __LOG2C(s) ((s &0xff00)             ? (8  +__LOG2D(s >>8)) : (__LOG2D(s)))
#define __LOG2D(s) ((s &0xf0)               ? (4  +__LOG2E(s >>4)) : (__LOG2E(s)))
#define __LOG2E(s) ((s &0xc)                ? (2  +__LOG2F(s >>2)) : (__LOG2F(s)))
#define __LOG2F(s) ((s &0x2)                ? (1)                  : (0))

#define LOG2_UINT64 __LOG2A
#define LOG2_UINT32 __LOG2B
#define LOG2_UINT16 __LOG2C
#define LOG2_UINT8  __LOG2D

static inline uint64_t
next_power_of_2(uint64_t i)
{
#if defined(__GNUC__)
    return 1UL <<(1 +(63 -__builtin_clzl(i -1)));
#else
    i =i -1;
    i =LOG2_UINT64(i);
    return 1UL <<(1 +i);
#endif
}

Jika Anda tidak ingin menjelajah ke ranah perilaku tidak terdefinisi, nilai input harus antara 1 dan 2 ^ 63. Makro juga berguna untuk menetapkan konstanta pada waktu kompilasi.


Ini mungkin solusi terburuk (sufiks ULL pada konstanta 64-bit juga hilang). Ini akan menghasilkan 32 tes per input dalam semua kasus. Lebih baik menggunakan loop sementara, itu akan selalu lebih cepat atau pada kecepatan yang sama.
xryl669

1
TAPI ... ini dapat dievaluasi oleh preprocessor jika inputnya konstan, dan dengan demikian NOL operasi pada waktu berjalan!
Michael

4

Untuk kelengkapan di sini adalah implementasi floating-point dalam standar rawa C.

double next_power_of_two(double value) {
    int exp;
    if(frexp(value, &exp) == 0.5) {
        // Omit this case to round precise powers of two up to the *next* power
        return value;
    }
    return ldexp(1.0, exp);
}

1
Browser acak, jika Anda membaca komentar ini, pilih kode ini. Ini jelas merupakan jawaban terbaik, tidak ada instruksi khusus, tidak ada twiddling, hanya efisien, portabel dan kode standar. Menebak mengapa tidak ada orang lain yang
membatalkannya

5
Browser acak, ini akan mati lambat jika Anda tidak memiliki perangkat keras floating point khusus. Pada x86 Anda dapat menjalankan lingkaran di sekitar kode ini menggunakan bit twiddling. rep bsr ecx,eax; mov eax,0; cmovnz eax,2; shl eax,clsekitar 25x lebih cepat.
Johan

4

Solusi spesifik Microsoft (mis. Visual Studio 2017) yang efisien dalam C / C ++ untuk input integer. Menangani case dari input yang sama persis dengan kekuatan dua nilai dengan mengurangi sebelum memeriksa lokasi 1 bit paling signifikan.

inline unsigned int ExpandToPowerOf2(unsigned int Value)
{
    unsigned long Index;
    _BitScanReverse(&Index, Value - 1);
    return (1U << (Index + 1));
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

#if defined(WIN64) // The _BitScanReverse64 intrinsic is only available for 64 bit builds because it depends on x64

inline unsigned long long ExpandToPowerOf2(unsigned long long Value)
{
    unsigned long Index;
    _BitScanReverse64(&Index, Value - 1);
    return (1ULL << (Index + 1));
}

#endif

Ini menghasilkan 5 atau lebih instruksi bergaris untuk prosesor Intel yang serupa dengan yang berikut:

dec eax
bsr rcx, rax
inc ecx
mov eax, 1
shl rax, cl

Tampaknya kompiler Visual Studio C ++ tidak dikodekan untuk mengoptimalkan ini untuk nilai waktu kompilasi, tetapi tidak seperti ada banyak instruksi di sana.

Edit:

Jika Anda ingin nilai input 1 menghasilkan 1 (2 pangkat zeroth), sedikit modifikasi pada kode di atas masih menghasilkan instruksi langsung tanpa cabang.

inline unsigned int ExpandToPowerOf2(unsigned int Value)
{
    unsigned long Index;
    _BitScanReverse(&Index, --Value);
    if (Value == 0)
        Index = (unsigned long) -1;
    return (1U << (Index + 1));
}

Hasilkan hanya beberapa instruksi lagi. Kuncinya adalah bahwa Indeks dapat diganti dengan tes diikuti oleh instruksi cmove.


Kesalahan kecil: Seharusnya mengembalikan 1 untuk 1, tetapi tidak.
0kcats

Terima kasih. Dalam aplikasi yang dikembangkannya kami secara eksplisit membutuhkan 2 pangkat pertama ketika 1 adalah input. Saya dapat dianggap sebagai kasus khusus dengan kondisi tanpa menghasilkan terlalu banyak instruksi yang saya bayangkan.
NoelC

Memperbarui jawaban untuk menyertakan versi yang mengembalikan 1 dengan nilai input 1.
NoelC

3

Di x86 Anda dapat menggunakan instruksi manipulasi sse4 bit untuk membuatnya cepat.

//assume input is in eax
popcnt edx,eax
lzcnt ecx,eax
cmp edx,1
jle @done       //popcnt says its a power of 2, return input unchanged
mov eax,2
shl eax,cl
@done: rep ret

Dalam c Anda dapat menggunakan intrinsik yang cocok.


Tidak berguna tapi Mengagumkan!
Marco

3

Inilah solusi saya di C. Semoga ini bisa membantu!

int next_power_of_two(int n) {
    int i = 0;
    for (--n; n > 0; n >>= 1) {
        i++;
    }
    return 1 << i;
}

0

Banyak arsitektur prosesor mendukung log base 2atau operasi yang sangat mirip - count leading zeros. Banyak kompiler memiliki intrinsik untuk itu. Lihat https://en.wikipedia.org/wiki/Find_first_set


ini bukan tentang menemukan set bit tertinggi (= bsr) atau menghitung nol di depan. ia ingin mengumpulkan hingga 2. kekuatan terdekat dari jawaban dengan "kurangi 1, lalu lakukan bsr dan geser 1 kiri" melakukan itu.
Flo

0

Dengan asumsi Anda memiliki kompiler yang baik & dapat melakukan sedikit twiddling sebelum tangan itu di atas saya pada saat ini, tetapi tetap ini bekerja !!!

    // http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogObvious
    #define SH1(v)  ((v-1) | ((v-1) >> 1))            // accidently came up w/ this...
    #define SH2(v)  ((v) | ((v) >> 2))
    #define SH4(v)  ((v) | ((v) >> 4))
    #define SH8(v)  ((v) | ((v) >> 8))
    #define SH16(v) ((v) | ((v) >> 16))
    #define OP(v) (SH16(SH8(SH4(SH2(SH1(v))))))         

    #define CB0(v)   ((v) - (((v) >> 1) & 0x55555555))
    #define CB1(v)   (((v) & 0x33333333) + (((v) >> 2) & 0x33333333))
    #define CB2(v)   ((((v) + ((v) >> 4) & 0xF0F0F0F) * 0x1010101) >> 24)
    #define CBSET(v) (CB2(CB1(CB0((v)))))
    #define FLOG2(v) (CBSET(OP(v)))

Kode tes di bawah ini:

#include <iostream>

using namespace std;

// http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogObvious
#define SH1(v)  ((v-1) | ((v-1) >> 1))  // accidently guess this...
#define SH2(v)  ((v) | ((v) >> 2))
#define SH4(v)  ((v) | ((v) >> 4))
#define SH8(v)  ((v) | ((v) >> 8))
#define SH16(v) ((v) | ((v) >> 16))
#define OP(v) (SH16(SH8(SH4(SH2(SH1(v))))))         

#define CB0(v)   ((v) - (((v) >> 1) & 0x55555555))
#define CB1(v)   (((v) & 0x33333333) + (((v) >> 2) & 0x33333333))
#define CB2(v)   ((((v) + ((v) >> 4) & 0xF0F0F0F) * 0x1010101) >> 24)
#define CBSET(v) (CB2(CB1(CB0((v)))))
#define FLOG2(v) (CBSET(OP(v))) 

#define SZ4         FLOG2(4)
#define SZ6         FLOG2(6)
#define SZ7         FLOG2(7)
#define SZ8         FLOG2(8) 
#define SZ9         FLOG2(9)
#define SZ16        FLOG2(16)
#define SZ17        FLOG2(17)
#define SZ127       FLOG2(127)
#define SZ1023      FLOG2(1023)
#define SZ1024      FLOG2(1024)
#define SZ2_17      FLOG2((1ul << 17))  // 
#define SZ_LOG2     FLOG2(SZ)

#define DBG_PRINT(x) do { std::printf("Line:%-4d" "  %10s = %-10d\n", __LINE__, #x, x); } while(0);

uint32_t arrTble[FLOG2(63)];

int main(){
    int8_t n;

    DBG_PRINT(SZ4);    
    DBG_PRINT(SZ6);    
    DBG_PRINT(SZ7);    
    DBG_PRINT(SZ8);    
    DBG_PRINT(SZ9); 
    DBG_PRINT(SZ16);
    DBG_PRINT(SZ17);
    DBG_PRINT(SZ127);
    DBG_PRINT(SZ1023);
    DBG_PRINT(SZ1024);
    DBG_PRINT(SZ2_17);

    return(0);
}

Output:

Line:39           SZ4 = 2
Line:40           SZ6 = 3
Line:41           SZ7 = 3
Line:42           SZ8 = 3
Line:43           SZ9 = 4
Line:44          SZ16 = 4
Line:45          SZ17 = 5
Line:46         SZ127 = 7
Line:47        SZ1023 = 10
Line:48        SZ1024 = 10
Line:49        SZ2_16 = 17

0

Saya mencoba untuk mendapatkan daya terdekat 2 yang lebih rendah dan membuat fungsi ini. Semoga itu membantu Anda. Baru saja dikalikan angka terendah terdekat kali 2 untuk mendapatkan kekuatan tertinggi terdekat 2

int nearest_upper_power(int number){
    int temp=number;
    while((number&(number-1))!=0){
        temp<<=1;
        number&=temp;
    }
    //Here number is closest lower power 
    number*=2;
    return number;
}

0

Diadaptasi jawaban Paul Dixon untuk Excel, ini berfungsi dengan baik.

 =POWER(2,CEILING.MATH(LOG(A1)/LOG(2)))

0

Varian jawaban @YannDroneaud hanya berlaku untuk x==1, hanya untuk pelat x86, kompiler, gcc, atau dentang:

__attribute__ ((const))
static inline uint32_t p2(uint32_t x)
{
#if 0
    assert(x > 0);
    assert(x <= ((UINT32_MAX/2) + 1));
#endif
  int clz;
  uint32_t xm1 = x-1;
  asm(
    "lzcnt %1,%0"
    :"=r" (clz)
    :"rm" (xm1)
    :"cc"
    );
    return 1 << (32 - clz);
}

0

Inilah yang saya gunakan untuk membuat ini menjadi ekspresi konstan, jika inputnya adalah ekspresi konstan.

#define uptopow2_0(v) ((v) - 1)
#define uptopow2_1(v) (uptopow2_0(v) | uptopow2_0(v) >> 1)
#define uptopow2_2(v) (uptopow2_1(v) | uptopow2_1(v) >> 2)
#define uptopow2_3(v) (uptopow2_2(v) | uptopow2_2(v) >> 4)
#define uptopow2_4(v) (uptopow2_3(v) | uptopow2_3(v) >> 8)
#define uptopow2_5(v) (uptopow2_4(v) | uptopow2_4(v) >> 16)

#define uptopow2(v) (uptopow2_5(v) + 1)  /* this is the one programmer uses */

Jadi misalnya, ekspresi seperti:

uptopow2(sizeof (struct foo))

akan dengan baik mengurangi ke konstan.



0

Konversikan menjadi float lalu gunakan .hex () yang menunjukkan representasi IEEE yang dinormalisasi.

>>> float(789).hex() '0x1.8a80000000000p+9'

Kemudian cukup ekstrak eksponen dan tambahkan 1.

>>> int(float(789).hex().split('p+')[1]) + 1 10

Dan angkatlah 2 menjadi kekuatan ini.

>>> 2 ** (int(float(789).hex().split('p+')[1]) + 1) 1024


Perhatikan bahwa jawaban ini menggunakan python
David Wallace

0
import sys


def is_power2(x):
    return x > 0 and ((x & (x - 1)) == 0)


def find_nearest_power2(x):
    if x <= 0:
        raise ValueError("invalid input")
    if is_power2(x):
        return x
    else:
        bits = get_bits(x)
        upper = 1 << (bits)
        lower = 1 << (bits - 1)
        mid = (upper + lower) // 2
        if (x - mid) > 0:
            return upper
        else:
            return lower


def get_bits(x):
    """return number of bits in binary representation"""
    if x < 0:
        raise ValueError("invalid input: input should be positive integer")
    count = 0
    while (x != 0):
        try:
            x = x >> 1
        except TypeError as error:
            print(error, "input should be of type integer")
            sys.exit(1)
        count += 1
    return count

-1

Jika Anda membutuhkannya untuk hal-hal terkait OpenGL:

/* Compute the nearest power of 2 number that is 
 * less than or equal to the value passed in. 
 */
static GLuint 
nearestPower( GLuint value )
{
    int i = 1;

    if (value == 0) return -1;      /* Error! */
    for (;;) {
         if (value == 1) return i;
         else if (value == 3) return i*4;
         value >>= 1; i *= 2;
    }
}

8
'for' adalah sebuah loop.
florin

1
florin: benar. dan itu digunakan sebagai loop di sini, bukan?
Tamas Czinege

9
DrJokepu - Saya pikir florin dimaksudkan untuk mengatakan di sini bahwa OP meminta solusi loop-kurang
Eli Bendersky

-1

Jika Anda menginginkan templat satu garis. Ini dia

int nxt_po2(int n) { return 1 + (n|=(n|=(n|=(n|=(n|=(n-=1)>>1)>>2)>>4)>>8)>>16); }

atau

int nxt_po2(int n) { return 1 + (n|=(n|=(n|=(n|=(n|=(n-=1)>>(1<<0))>>(1<<1))>>(1<<2))>>(1<<3))>>(1<<4)); }

Ini adalah perilaku yang tidak terdefinisi dalam C atau C ++ dan akan menyebabkan kesalahan. Memodifikasi nbeberapa kali tanpa titik urutan tidak valid. Anda menulisnya seolah-olah n-=1harus terjadi terlebih dahulu tetapi satu-satunya jaminan di sini adalah yang nberisi nilai baru setelah ;tanda kurung dan tidak mengubah itu.
sam hocevar

Lebih tepatnya, itu membuat mata saya berdarah.
Donal Fellows
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.