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?
c
optimization
bit-manipulation
Naveen
sumber
sumber
Jawaban:
Periksa Bit Twiddling Hacks . Anda perlu mendapatkan logaritma basis 2, lalu tambahkan 1 untuk itu. Contoh untuk nilai 32-bit:
Perpanjangan ke lebar lain harus jelas.
sumber
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.x > UINT32_MAX
dan tidak bercabang. Juga, GCC dan Dentang digunakan-mtune=generic
secara default (seperti kebanyakan distro), jadi kode Anda TIDAK akan meluas kelzcnt
instruksi 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.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?
sumber
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?sumber
uint32_t
.Saya pikir ini juga berfungsi:
Dan jawabannya adalah
power
.sumber
power <<= 1
x
terlalu besar (mis. Tidak cukup bit untuk mewakili kekuatan 2 berikutnya).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 instruksibsr
(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
sumber
_BitScanForward
Visual C ++__builtin_ctz()
__builtin_ctz()
tidak akan berguna untuk membulatkan non power dari 2 angka hingga kekuatan dua berikutnyaconstexpr uint64_t nextPowerOfTwo64 (uint64_t x) { return 1ULL<<(sizeof(uint64_t) * 8 - __builtin_clzll(x)); }
Satu lagi, meskipun saya menggunakan siklus, tetapi ini jauh lebih cepat daripada operan matematika
kekuatan dua opsi "lantai":
kekuatan dua opsi "ceil":
MEMPERBARUI
Seperti disebutkan dalam komentar ada kesalahan di
ceil
mana hasilnya salah.Berikut adalah fungsi lengkapnya:
sumber
x
kekuatan 2. Mikro untuk menguji apakah input adalah kekuatan 2 diperlukan.#define ISPOW2(x) ((x) > 0 && !((x) & (x-1)))
if (x == 0) return 1; /* Or 0 (Which is what I use) */ x--; /* Rest of program */
power of two "ceil" option
itu tidak benar. Misalnya, ketikax = 2
hasilnya seharusnya2
bukan4
Untuk jenis yang tidak ditandatangani, membangun Bit Twiddling Hacks:
Sebenarnya tidak ada loop di sana sebagai kompiler tahu pada waktu kompilasi jumlah iterasi.
sumber
std::is_unsigned<UnsignedType>::value
pernyataan tersebut.Untuk pelampung IEEE Anda bisa melakukan hal seperti ini.
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.
sumber
Meskipun pertanyaannya ditandai karena di
c
sini lima sen saya. Beruntung kami, C ++ 20 akan mencakupstd::ceil2
danstd::floor2
(lihat di sini ). Ini adalahconsexpr
fungsi template, implementasi GCC saat ini menggunakan bitshifting dan bekerja dengan semua tipe unsigned integral.sumber
bit_ceil
open-std.org/JTC1/SC22/WG21/docs/papers/2020/p1956r1.pdfJika 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.
sumber
Untuk kelengkapan di sini adalah implementasi floating-point dalam standar rawa C.
sumber
rep bsr ecx,eax; mov eax,0; cmovnz eax,2; shl eax,cl
sekitar 25x lebih cepat.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.
Ini menghasilkan 5 atau lebih instruksi bergaris untuk prosesor Intel yang serupa dengan yang berikut:
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.
Hasilkan hanya beberapa instruksi lagi. Kuncinya adalah bahwa Indeks dapat diganti dengan tes diikuti oleh instruksi cmove.
sumber
Di x86 Anda dapat menggunakan instruksi manipulasi sse4 bit untuk membuatnya cepat.
Dalam c Anda dapat menggunakan intrinsik yang cocok.
sumber
Inilah solusi saya di C. Semoga ini bisa membantu!
sumber
Banyak arsitektur prosesor mendukung
log base 2
atau operasi yang sangat mirip -count leading zeros
. Banyak kompiler memiliki intrinsik untuk itu. Lihat https://en.wikipedia.org/wiki/Find_first_setsumber
Dengan asumsi Anda memiliki kompiler yang baik & dapat melakukan sedikit twiddling sebelum tangan itu di atas saya pada saat ini, tetapi tetap ini bekerja !!!
Kode tes di bawah ini:
Output:
sumber
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
sumber
Diadaptasi jawaban Paul Dixon untuk Excel, ini berfungsi dengan baik.
sumber
Varian jawaban @YannDroneaud hanya berlaku untuk
x==1
, hanya untuk pelat x86, kompiler, gcc, atau dentang:sumber
Inilah yang saya gunakan untuk membuat ini menjadi ekspresi konstan, jika inputnya adalah ekspresi konstan.
Jadi misalnya, ekspresi seperti:
akan dengan baik mengurangi ke konstan.
sumber
Anda mungkin menemukan klarifikasi berikut untuk membantu tujuan Anda:
sumber
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
sumber
sumber
Jika Anda membutuhkannya untuk hal-hal terkait OpenGL:
sumber
Jika Anda menginginkan templat satu garis. Ini dia
atau
sumber
n
beberapa kali tanpa titik urutan tidak valid. Anda menulisnya seolah-olahn-=1
harus terjadi terlebih dahulu tetapi satu-satunya jaminan di sini adalah yangn
berisi nilai baru setelah;
tanda kurung dan tidak mengubah itu.