ฉันต้องการเขียนฟังก์ชั่นที่คืนค่ากำลังสองถัดไปที่ใกล้ที่สุด ตัวอย่างเช่นถ้าอินพุตของฉันคือ 789 ผลลัพธ์ควรเป็น 1024 มีวิธีใดบ้างที่จะบรรลุผลนี้โดยไม่ใช้ลูปใด ๆ แต่ใช้ตัวดำเนินการระดับบิต?
ฉันต้องการเขียนฟังก์ชั่นที่คืนค่ากำลังสองถัดไปที่ใกล้ที่สุด ตัวอย่างเช่นถ้าอินพุตของฉันคือ 789 ผลลัพธ์ควรเป็น 1024 มีวิธีใดบ้างที่จะบรรลุผลนี้โดยไม่ใช้ลูปใด ๆ แต่ใช้ตัวดำเนินการระดับบิต?
คำตอบ:
ตรวจสอบ บิต twiddling Hacks คุณต้องได้ลอการิทึมฐาน 2 แล้วเพิ่ม 1 ลงไป ตัวอย่างสำหรับค่า 32 บิต:
ปัดเศษขึ้นเป็นกำลังสูงสุดถัดไปที่ 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++;
ส่วนขยายไปยังความกว้างอื่น ๆ ควรชัดเจน
uint64_t next_pow2(uint64_t x) { return x == 1 ? 1 : 1<<(64-__builtin_clzl(x-1)); }
และสำหรับ 32 บิต: uint32_t next_pow2(uint32_t x) { return x == 1 ? 1 : 1<<(32-__builtin_clz(x-1)); }
นั่นคือถ้าคุณใช้ GCC (และฉันคิดอย่างงุ่มง่าม ?) แต่มันควรจะใช้เวลาในการ ค้นหาการเรียกไปยัง CLZ แทนการคัดลอกวางตัวเลือกทั้งหมด
x > UINT32_MAX
และไม่ได้ไร้สาขา นอกจากนี้ GCC และเสียงดังกราวการใช้งาน-mtune=generic
โดยค่าเริ่มต้น (เช่นการทำ distros ส่วนใหญ่) ดังนั้นรหัสของคุณจะไม่ขยายไปที่lzcnt
การเรียนการสอนใน x86_64 - มันจริงจะขยายไปยังสิ่งที่ช้ามาก (ประจำ libgcc) -march=native
เว้นแต่คุณจะใช้สิ่งที่ต้องการ ดังนั้นการแทนที่ที่คุณเสนอจึงไม่ใช่แบบพกพาบั๊กกี้และ (โดยทั่วไป) ช้ากว่า
next = pow(2, ceil(log(x)/log(2)));
วิธีนี้ใช้งานได้โดยค้นหาหมายเลขที่คุณต้องการยกระดับ 2 ด้วยเพื่อรับ x (นำบันทึกของตัวเลขมาหารด้วยบันทึกของฐานที่ต้องการดูวิกิพีเดียสำหรับข้อมูลเพิ่มเติม ) จากนั้นปัดเศษขึ้นด้วยเพดานเพื่อให้ได้พลังงานจำนวนเต็มที่ใกล้ที่สุด
นี่เป็นจุดประสงค์ทั่วไป (เช่นช้ากว่า!) วิธีกว่าวิธี bitwise ที่เชื่อมโยงที่อื่น แต่ดีที่จะรู้คณิตศาสตร์ใช่มั้ย?
log(pow(2,29))/log(2)
= 29.000000000000004 ดังนั้นผลลัพธ์คือ 2 30 แทนที่จะส่งกลับ 2 29 ฉันคิดว่านี่เป็นเหตุผลว่าทำไมฟังก์ชั่น log2 จึงมีอยู่?
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
ฟังก์ชั่นนี้ควรใช้เวลาและการส่งกลับ
ฉันคิดว่ามันใช้ได้ดีเช่นกัน:
int power = 1;
while(power < x)
power*=2;
power
และคำตอบคือ
power <<= 1
x
มีขนาดใหญ่เกินไป (เช่นบิตไม่เพียงพอที่จะเป็นตัวแทนของพลังต่อไปของ 2)
หากคุณใช้ GCC คุณอาจต้องการดูที่การเพิ่มประสิทธิภาพฟังก์ชั่น next_pow2 ()โดย Lockless Inc. หน้านี้อธิบายวิธีการใช้ฟังก์ชั่นในตัวbuiltin_clz()
(นับศูนย์นำหน้า) และใช้ในภายหลังโดยตรง x86 (ia32) การเรียนการสอนประกอบbsr
(สแกนบิตกลับ) เช่นเดียวกับมันอธิบายไว้ในคำตอบอื่น 's ลิงค์ไปยังเว็บไซต์ gamedev รหัสนี้อาจจะเร็วกว่าที่อธิบายไว้ในคำตอบก่อนหน้า
อย่างไรก็ตามถ้าคุณไม่ต้องการใช้คำสั่งแอสเซมเบลอร์และชนิดข้อมูล 64 บิตคุณสามารถใช้สิ่งนี้ได้
/**
* 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 ใด ๆ ไปจนถึงกำลังสองถัดไป
constexpr uint64_t nextPowerOfTwo64 (uint64_t x) { return 1ULL<<(sizeof(uint64_t) * 8 - __builtin_clzll(x)); }
อีกอย่างหนึ่งแม้ว่าฉันจะใช้วงจร แต่ thi นั้นเร็วกว่าตัวถูกดำเนินการทางคณิตศาสตร์
พลังของสองตัวเลือก "ชั้น":
int power = 1;
while (x >>= 1) power <<= 1;
พลังของสองตัวเลือก "เพดาน":
int power = 2;
x--; // <<-- UPDATED
while (x >>= 1) power <<= 1;
UPDATE
ตามที่ระบุไว้ในความคิดเห็นมีข้อผิดพลาดในceil
ที่ที่ผลลัพธ์นั้นผิด
นี่คือฟังก์ชั่นเต็มรูปแบบ:
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
มีกำลัง 2 ไมโครเพื่อทดสอบว่าจำเป็นต้องมีอินพุตกำลัง 2 #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
ไม่ถูกต้อง ตัวอย่างเช่นเมื่อx = 2
ผลที่ควรจะ2
แทน4
สำหรับประเภทที่ไม่ได้ลงนามใด ๆ การสร้าง Bit Hiddles:
#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;
}
ไม่มีการวนซ้ำที่แท้จริงเนื่องจากคอมไพเลอร์ทราบเวลารวบรวมจำนวนการวนซ้ำ
std::is_unsigned<UnsignedType>::value
ยืนยัน
สำหรับ IEEE ลอยคุณจะสามารถทำสิ่งนี้ได้
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));
}
หากคุณต้องการวิธีแก้ปัญหาจำนวนเต็มและคุณสามารถใช้ชุดประกอบแบบอินไลน์ BSR จะให้ log2 ของจำนวนเต็มใน x86 มันนับจำนวนบิตที่ถูกตั้งค่าซึ่งเท่ากับ log2 ของตัวเลขนั้น ตัวประมวลผลอื่นมีคำแนะนำที่คล้ายกัน (บ่อยครั้ง) เช่น CLZ และขึ้นอยู่กับคอมไพเลอร์ของคุณอาจมีตัวประมวลผลที่พร้อมใช้งานสำหรับคุณ
แม้จะมีคำถามถูกติดแท็กเหมือนc
ที่นี่ห้าเซ็นต์ของฉัน โชคดีที่ C ++ 20 น่าจะรวมstd::ceil2
และstd::floor2
(ดูที่นี่ ) มันเป็นconsexpr
ฟังก์ชั่นเทมเพลตการใช้GCCในปัจจุบันใช้การเปลี่ยนบิตและทำงานกับประเภทที่ไม่ได้ลงนามใด ๆ
bit_ceil
open-std.org/JTC1/SC22/WG21/docs/papers/2020/p1956r1.pdf
/*
** 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
}
หากคุณไม่ต้องการเสี่ยงกับพฤติกรรมที่ไม่ได้กำหนดค่าอินพุตจะต้องอยู่ระหว่าง 1 ถึง 2 ^ 63 แมโครยังมีประโยชน์ในการตั้งค่าคงที่ ณ เวลารวบรวม
เพื่อความสมบูรณ์ที่นี่คือการใช้จุดลอยตัวใน bog-standard 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);
}
rep bsr ecx,eax; mov eax,0; cmovnz eax,2; shl eax,cl
เร็วกว่าประมาณ 25 เท่า
โซลูชันเฉพาะของ Microsoft (เช่น Visual Studio 2017) ที่มีประสิทธิภาพใน C / C ++ สำหรับอินพุตจำนวนเต็ม จัดการกรณีของอินพุตที่ตรงกับพลังของสองค่าโดยการลดลงก่อนที่จะตรวจสอบตำแหน่งของ 1 บิตที่สำคัญที่สุด
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
สิ่งนี้จะสร้างคำแนะนำแบบอินไลน์ 5 คำหรือมากกว่าสำหรับโปรเซสเซอร์ Intel ที่คล้ายกับต่อไปนี้:
dec eax
bsr rcx, rax
inc ecx
mov eax, 1
shl rax, cl
เห็นได้ชัดว่าคอมไพเลอร์ Visual Studio C ++ ไม่ได้รับการเข้ารหัสเพื่อปรับค่านี้ให้เหมาะสมสำหรับค่าเวลาคอมไพล์ แต่ไม่เหมือนว่ามีคำแนะนำมากมาย
แก้ไข:
หากคุณต้องการให้ค่าอินพุต 1 ให้ผล 1 (2 ถึงกำลัง zeroth) การปรับเปลี่ยนเล็กน้อยในโค้ดข้างต้นจะยังคงสร้างคำสั่งแบบตรงโดยไม่มีสาขา
inline unsigned int ExpandToPowerOf2(unsigned int Value)
{
unsigned long Index;
_BitScanReverse(&Index, --Value);
if (Value == 0)
Index = (unsigned long) -1;
return (1U << (Index + 1));
}
สร้างคำแนะนำเพิ่มเติมอีกไม่กี่คำ เคล็ดลับคือดัชนีสามารถถูกแทนที่ด้วยการทดสอบตามด้วยคำสั่ง cmove
ใน x86 คุณสามารถใช้คำสั่งการจัดการ sse4 บิตเพื่อทำให้รวดเร็ว
//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 คุณสามารถใช้การจับคู่ที่แท้จริง
นี่คือทางออกของฉันใน C. หวังว่านี่จะช่วยได้!
int next_power_of_two(int n) {
int i = 0;
for (--n; n > 0; n >>= 1) {
i++;
}
return 1 << i;
}
สถาปัตยกรรมโปรเซสเซอร์หลายตัวรองรับlog base 2
หรือดำเนินการคล้ายกันมาก - count leading zeros
. คอมไพเลอร์หลายคนมีความเป็นอยู่ที่แท้จริง ดูhttps://en.wikipedia.org/wiki/Find_first_set
สมมติว่าคุณมีคอมไพเลอร์ที่ดี & มันสามารถทำอะไรนิด ๆ หน่อย ๆ ก่อนที่มือที่เหนือฉันที่จุดนี้ แต่ต่อไปนี้ใช้งานได้ !!!
// 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)))
รหัสทดสอบด้านล่าง:
#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);
}
ขาออก:
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
ฉันกำลังพยายามที่จะลดกำลังของ 2 ที่ใกล้ที่สุดและทำฟังก์ชั่นนี้ ขอให้มันช่วยคุณได้แค่คูณจำนวนที่ต่ำกว่าที่ใกล้ที่สุดคูณ 2 เพื่อให้ได้พลังงานที่ใกล้ที่สุด 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;
}
ดัดแปลงคำตอบของ Paul Dixon สู่ Excel สิ่งนี้ใช้ได้อย่างสมบูรณ์แบบ
=POWER(2,CEILING.MATH(LOG(A1)/LOG(2)))
ตัวแปรของ @YannDroneaud ตอบถูกต้องสำหรับx==1
เฉพาะสำหรับ x86 plateforms, คอมไพเลอร์, gcc หรือเสียงดังกราว:
__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);
}
นี่คือสิ่งที่ฉันใช้เพื่อให้นี่เป็นนิพจน์คงที่หากอินพุตเป็นนิพจน์คงที่
#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 */
ตัวอย่างเช่นการแสดงออกเช่น:
uptopow2(sizeof (struct foo))
จะลดลงอย่างคงที่
แปลงเป็นทศนิยมแล้วใช้. hex () ซึ่งแสดงการแทนค่า IEEE ปกติ
>>> float(789).hex()
'0x1.8a80000000000p+9'
จากนั้นก็แยกเลขชี้กำลังและเพิ่ม 1
>>> int(float(789).hex().split('p+')[1]) + 1
10
และยกกำลัง 2 นี้ให้ได้
>>> 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:
/* 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;
}
}
ถ้าคุณต้องการหนึ่งบรรทัดแม่แบบ นี่มันคือ
int nxt_po2(int n) { return 1 + (n|=(n|=(n|=(n|=(n|=(n-=1)>>1)>>2)>>4)>>8)>>16); }
หรือ
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
หลายครั้งโดยไม่มีจุดลำดับไม่ถูกต้อง คุณเขียนราวกับว่าn-=1
ควรจะเกิดขึ้นก่อน แต่การรับประกันเพียงอย่างเดียวที่นี่คือที่n
มีค่าใหม่หลังจากที่;
และวงเล็บไม่เปลี่ยนแปลง