2 numaraya en yakın sonraki gücü veren bir fonksiyon yazmak istiyorum. Örneğin, girdim 789 ise, çıktı 1024 olmalıdır. Bunu herhangi bir döngü kullanmadan, sadece bitsel operatörler kullanarak gerçekleştirmenin herhangi bir yolu var mı?
2 numaraya en yakın sonraki gücü veren bir fonksiyon yazmak istiyorum. Örneğin, girdim 789 ise, çıktı 1024 olmalıdır. Bunu herhangi bir döngü kullanmadan, sadece bitsel operatörler kullanarak gerçekleştirmenin herhangi bir yolu var mı?
Yanıtlar:
Bit Twiddling Hack'lerini kontrol edin . Temel 2 logaritmasını almanız ve ardından buna 1 eklemeniz gerekir. 32 bit değerine örnek:
Bir sonraki en yüksek güç 2'ye yuvarlayın
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++;
Diğer genişliklere olan uzantı açık olmalıdır.
uint64_t next_pow2(uint64_t x) { return x == 1 ? 1 : 1<<(64-__builtin_clzl(x-1)); }
Ve 32 bit için: uint32_t next_pow2(uint32_t x) { return x == 1 ? 1 : 1<<(32-__builtin_clz(x-1)); }
Bu, GCC'yi (ve sanırım Clang'ı kullanırsanız), ancak zaman ayırmak akıllıca olur çevresindeki tüm seçenekleri kopyalamak yerine CLZ çağrısını bulun.
x > UINT32_MAX
dalsız değilse tanımsız bir davranışa sahiptir . Ayrıca, GCC ve Clang -mtune=generic
varsayılan olarak kullanılır (çoğu dağıtımda olduğu gibi), bu nedenle kodunuz lzcnt
x86_64'teki talimatlara genişlemez - böyle bir şey kullanmazsanız aslında çok daha yavaş bir şeye (libgcc rutin) genişler -march=native
. Bu nedenle, önerilen değiştirme işleminiz taşınabilir olmayan, buggy ve (genellikle) daha yavaştır.
next = pow(2, ceil(log(x)/log(2)));
Bu, x elde etmek için 2 artıracağınız sayıyı bularak çalışır (sayının günlüğünü alın ve istenen tabanın günlüğüne bölün, daha fazla bilgi için wikipedia'ya bakın ). Sonra en yakın tam sayı gücünü elde etmek için tavana yuvarlayın.
Bu, başka bir yerde bağlantılı bitsel yöntemlerden daha genel bir amaç (yani daha yavaş!) Bir yöntemdir, ancak matematiği bilmek iyidir, ha?
log(pow(2,29))/log(2)
= 29.000000000000004, sonuç 2 29 döndürmek yerine 2 30'dur . Bence log2 fonksiyonları bu yüzden var mı?
Bence bu da işe yarıyor:
int power = 1;
while(power < x)
power*=2;
Ve cevap power
.
power <<= 1
x
Çok büyükse sonsuz döngüye dikkat edin (örn. 2'nin sonraki gücünü temsil etmek için yeterli bit yok).
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;
}
uint32_t
.
GCC kullanıyorsanız, Lockless Inc. tarafından next_pow2 () işlevini en iyi duruma getirme konusuna göz atmak isteyebilirsiniz . Bu sayfada yerleşik işlev builtin_clz()
(sıfır önde sayma) ve daha sonra doğrudan x86 (ia32) kullanımı montajcı talimat bsr
bu bölümde açıklanan gibi (biraz tarama ters), başka bir yanıt 'ın GameDev sitesine bağlantı . Bu kod, önceki yanıtta açıklanan kodlardan daha hızlı olabilir .
Bu arada, montajcı talimatını ve 64 bit veri türünü kullanmayacaksanız, bunu kullanabilirsiniz
/**
* 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));
}
_BitScanForward
Visual C ++
__builtin_ctz()
__builtin_ctz()
, 2 sayının herhangi bir gücünü ikisinin bir sonraki gücüne yuvarlamak için yararlı olmayacaktır
constexpr uint64_t nextPowerOfTwo64 (uint64_t x) { return 1ULL<<(sizeof(uint64_t) * 8 - __builtin_clzll(x)); }
Bir tane daha, döngü kullanmama rağmen, matematik işlenenlerinden çok daha hızlı
iki "kat" seçeneğinin gücü:
int power = 1;
while (x >>= 1) power <<= 1;
iki "tavan" seçeneğinin gücü:
int power = 2;
x--; // <<-- UPDATED
while (x >>= 1) power <<= 1;
GÜNCELLEME
Yorumlarda belirtildiği gibi ceil
, sonucunun yanlış olduğu yerde hata vardı .
İşte tam fonksiyonlar:
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;
}
x
güç 2 ise sonuç doğru değildir. Giriş gücü 2 olup olmadığını test etmek için bir mikro gereklidir. #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
doğru değil. Örneğin, x = 2
sonucu olmalıdır 2
yerine4
İmzasız herhangi bir tür için, Bit Twiddling Hack'lerini temel alan:
#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;
}
Derleyici gerçekten yineleme sayısını bildiği için orada bir döngü yoktur.
std::is_unsigned<UnsignedType>::value
iddiayı göz ardı ederek genişletebileceğinden eminim .
IEEE şamandıraları için böyle bir şey yapabilirsiniz.
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));
}
Bir tamsayı çözümüne ihtiyacınız varsa ve satır içi montajı kullanabiliyorsanız, BSR size x86 üzerinde bir tamsayının log2'sini verecektir. Kaç tane doğru bitin ayarlandığını sayar, bu sayı tam olarak bu sayının log2 değerine eşittir. Diğer işlemciler CLZ gibi benzer talimatlara sahiptir (genellikle) ve derleyicinize bağlı olarak işi sizin için yapabilecek bir içsel olabilir.
Rağmen c
burada olduğu gibi etiketlenmiş beş sent. Şanslıyız, C ++ 20 içerir std::ceil2
ve std::floor2
( buraya bakınız ). Öyle consexpr
şablon işlevleri, cari GCC uygulaması herhangi ayrılmaz işaretsiz tip kullanımlara bitshifting ve eserleri.
bit_ceil
open-std.org/JTC1/SC22/WG21/docs/papers/2020/p1956r1.pdf olarak
/*
** 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
}
Tanımsız davranış alanına girmek istemiyorsanız, giriş değeri 1 ile 2 ^ 63 arasında olmalıdır. Makro, derleme zamanında sabit ayarlamak için de yararlıdır.
Bütünlük için burada bataklık standart C'de kayan nokta uygulamasıdır.
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);
}
rep bsr ecx,eax; mov eax,0; cmovnz eax,2; shl eax,cl
yaklaşık 25 kat daha hızlıdır.
Tamsayı girişi için C / C ++ 'da etkili Microsoft'a (ör. Visual Studio 2017) özgü bir çözüm. En önemli 1 bitin konumunu kontrol etmeden önce, giriş değerini iki değerlik bir güçle tam olarak eşleştirerek azaltarak işleme koyar.
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
Bu, aşağıdakilere benzer bir Intel işlemci için 5 veya daha fazla satır içi talimat oluşturur:
dec eax
bsr rcx, rax
inc ecx
mov eax, 1
shl rax, cl
Görünüşe göre Visual Studio C ++ derleyicisi bunu derleme zamanı değerleri için optimize etmek için kodlanmamış, ancak orada bir sürü talimat var gibi değil.
Düzenle:
1 giriş değerinin 1 (sıfırıncı güce 2) vermesini istiyorsanız, yukarıdaki kodda küçük bir değişiklik yapılsa da dalsız talimatlar doğrudan doğruya üretilir.
inline unsigned int ExpandToPowerOf2(unsigned int Value)
{
unsigned long Index;
_BitScanReverse(&Index, --Value);
if (Value == 0)
Index = (unsigned long) -1;
return (1U << (Index + 1));
}
Birkaç talimat daha oluşturur. İşin püf noktası, Dizin'in bir test ve ardından bir cmove talimatı ile değiştirilebilmesidir.
X86'da, hızlı yapmak için sse4 bit manipülasyon talimatlarını kullanabilirsiniz.
//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
C ile eşleşen intrinsics kullanabilirsiniz.
İşte C'deki çözümüm. Umarım bu yardımcı olur!
int next_power_of_two(int n) {
int i = 0;
for (--n; n > 0; n >>= 1) {
i++;
}
return 1 << i;
}
Birçok işlemci mimarisi log base 2
veya çok benzer bir işlemi destekler count leading zeros
. Birçok derleyici bunun için kendine has özelliklere sahiptir. Bkz. Https://en.wikipedia.org/wiki/Find_first_set
Eğer iyi bir derleyici var varsayalım & bu noktada üstümde şu elden önce biraz twiddling yapabilir, ama yine de bu çalışıyor !!!
// 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)))
Aşağıdaki test kodu:
#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);
}
Çıktılar:
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
En yakın düşük 2 gücü almaya çalışıyorum ve bu işlevi yaptım. Size yardımcı olabilir. 2'ye en yakın üst gücü elde etmek için en yakın düşük sayı 2'yi çarpın
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;
}
Paul Dixon'un Excel'e cevabı uyarlandı, bu mükemmel çalışıyor.
=POWER(2,CEILING.MATH(LOG(A1)/LOG(2)))
@YannDroneaud yanıtının bir çeşidi x==1
, yalnızca x86 plaka formları , derleyiciler, gcc veya clang için geçerlidir:
__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);
}
Giriş sabit bir ifade ise, bu sabit bir ifade olması için kullanıyorum.
#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 */
Örneğin, şöyle bir ifade:
uptopow2(sizeof (struct foo))
bir sabite indirgeyecek.
Bir kayan noktaya dönüştürün ve sonra normalleştirilmiş IEEE gösterimini gösteren .hex () yöntemini kullanın.
>>> float(789).hex()
'0x1.8a80000000000p+9'
Sonra üssü çıkarın ve 1 ekleyin.
>>> int(float(789).hex().split('p+')[1]) + 1
10
Ve bu güce 2 yükseltin.
>>> 2 ** (int(float(789).hex().split('p+')[1]) + 1)
1024
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
OpenGL ile ilgili şeyler için ihtiyacınız varsa:
/* 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;
}
}
Tek satırlık bir şablon istiyorsanız. İşte burada
int nxt_po2(int n) { return 1 + (n|=(n|=(n|=(n|=(n|=(n-=1)>>1)>>2)>>4)>>8)>>16); }
veya
int nxt_po2(int n) { return 1 + (n|=(n|=(n|=(n|=(n|=(n-=1)>>(1<<0))>>(1<<1))>>(1<<2))>>(1<<3))>>(1<<4)); }
n
bir dizi noktası olmadan defalarca geçersiz. n-=1
İlk önce olması gerektiği gibi yazdınız, ancak buradaki tek garanti, n
sonradan yeni değerini içeren ;
ve parantezlerin bunu değiştirmediğidir.