การปัดเศษขึ้นเป็นกำลังต่อไปของ 2


189

ฉันต้องการเขียนฟังก์ชั่นที่คืนค่ากำลังสองถัดไปที่ใกล้ที่สุด ตัวอย่างเช่นถ้าอินพุตของฉันคือ 789 ผลลัพธ์ควรเป็น 1024 มีวิธีใดบ้างที่จะบรรลุผลนี้โดยไม่ใช้ลูปใด ๆ แต่ใช้ตัวดำเนินการระดับบิต?


4
ดูที่นี่สำหรับวิธีแก้ปัญหาที่เป็นไปได้: http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2Float
Stefan

4
คุณต้องการพลังงานที่ใกล้ที่สุดเท่ากับ 2 (เช่น 65 จะให้ 64 แต่ 100 จะให้ 128) หรือใกล้เคียงข้างต้น (เช่น 65 จะให้คุณ 128 และ 100 จะได้ไหม)
Kim Reece

1
พวกเขามีคำถามหลายข้อที่ตรงกับคำถามนี้ ตัวอย่างเช่น: stackoverflow.com/questions/364985/…
Yann Droneaud


7
@ นาธานลิงค์ของคุณช้ากว่าคำถามนี้8 เดือน
โจเซฟ Quinsey

คำตอบ:


148

ตรวจสอบ บิต 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++;

ส่วนขยายไปยังความกว้างอื่น ๆ ควรชัดเจน


11
นี่ไม่ใช่วิธีที่มีประสิทธิภาพที่สุดเพราะโปรเซสเซอร์หลายตัวมีคำสั่งพิเศษสำหรับการนับเลขศูนย์นำหน้าซึ่งสามารถใช้ในการคำนวณ log2 ได้อย่างมีประสิทธิภาพมาก ดูen.wikipedia.org/wiki/Find_first_set
Simon

7
@Simon: มันเป็นโซลูชันแบบพกพา ไม่มีอัลกอริทึมที่มีประสิทธิภาพทั่วไปสำหรับสถาปัตยกรรมทั้งหมด
phuclv

5
ถ้าจำนวนตัวเองเป็นพลังของสองคืออะไร?
Litherum

5
เธรดนี้ยังคงมีการอ้างอิงที่ดี แต่คำตอบนี้ (และอื่น ๆ ส่วนใหญ่) ล้าสมัยอย่างมาก ซีพียูมีคำสั่งให้ช่วยเหลือสิ่งนี้ (จริงๆแล้วในเวลานั้น?) จาก: 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)); }และสำหรับ 32 บิต: uint32_t next_pow2(uint32_t x) { return x == 1 ? 1 : 1<<(32-__builtin_clz(x-1)); }นั่นคือถ้าคุณใช้ GCC (และฉันคิดอย่างงุ่มง่าม ?) แต่มันควรจะใช้เวลาในการ ค้นหาการเรียกไปยัง CLZ แทนการคัดลอกวางตัวเลือกทั้งหมด
MappaM

2
@MappaM คำตอบนี้ยังมีความเกี่ยวข้องสูงและเป็นวิธีพกพาที่ดีที่สุด เวอร์ชัน 64 บิตของคุณมีพฤติกรรมที่ไม่ได้กำหนดหากx > UINT32_MAXและไม่ได้ไร้สาขา นอกจากนี้ GCC และเสียงดังกราวการใช้งาน-mtune=genericโดยค่าเริ่มต้น (เช่นการทำ distros ส่วนใหญ่) ดังนั้นรหัสของคุณจะไม่ขยายไปที่lzcntการเรียนการสอนใน x86_64 - มันจริงจะขยายไปยังสิ่งที่ช้ามาก (ประจำ libgcc) -march=nativeเว้นแต่คุณจะใช้สิ่งที่ต้องการ ดังนั้นการแทนที่ที่คุณเสนอจึงไม่ใช่แบบพกพาบั๊กกี้และ (โดยทั่วไป) ช้ากว่า
Craig Barnes

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

วิธีนี้ใช้งานได้โดยค้นหาหมายเลขที่คุณต้องการยกระดับ 2 ด้วยเพื่อรับ x (นำบันทึกของตัวเลขมาหารด้วยบันทึกของฐานที่ต้องการดูวิกิพีเดียสำหรับข้อมูลเพิ่มเติม ) จากนั้นปัดเศษขึ้นด้วยเพดานเพื่อให้ได้พลังงานจำนวนเต็มที่ใกล้ที่สุด

นี่เป็นจุดประสงค์ทั่วไป (เช่นช้ากว่า!) วิธีกว่าวิธี bitwise ที่เชื่อมโยงที่อื่น แต่ดีที่จะรู้คณิตศาสตร์ใช่มั้ย?


3
จาก C99 คุณสามารถใช้log2 ได้หากเครื่องมือของคุณรองรับ GCC และ VS ดูเหมือนจะไม่ :(
Matthew อ่าน

2
คุณไม่พบวงเล็บ ... next = pow (2, ceil (log (x) / log (2)));
Matthieu Cormier

13
ระวังเรื่องความแม่นยำในการลอย log(pow(2,29))/log(2)= 29.000000000000004 ดังนั้นผลลัพธ์คือ 2 30 แทนที่จะส่งกลับ 2 29 ฉันคิดว่านี่เป็นเหตุผลว่าทำไมฟังก์ชั่น log2 จึงมีอยู่?
endolith

48
ค่าใช้จ่ายนี้น่าจะเป็นอย่างน้อย 200 รอบและไม่ถูกต้อง ทำไมถึงมี upvotes มากมาย?
Axel Gneiting

4
@ SuperflyJon แต่มันพูดถึงผู้ประกอบการระดับบิตและฉันคิดว่าความถูกต้องจะถูกระบุโดยคำถามใด ๆ เว้นแต่จะระบุไว้เป็นอย่างอื่น
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
จะดีถ้าคุณได้ประกอบมัน (เว้นแต่คุณจะค้นพบมัน) มันมาจากหน้าแฮ็กบิตสองบิต
florin

3
นั่นเป็นตัวเลข 32 บิตหรือเปล่า? ส่วนขยายสำหรับ 64-bit?
Jonathan Leffler

โจนาธานคุณต้องทำเพื่อครึ่งบนและถ้านั่นเป็นศูนย์คุณจะทำเพื่อครึ่งล่าง
florin

5
@florin ถ้า v เป็นชนิด 64 บิตคุณไม่สามารถเพิ่ม "c | = v >> 32" หลังจากอันที่ 16 หรือไม่?
Evan Teran

3
รหัสที่ใช้ได้กับความกว้างบิตเฉพาะนั้นควรใช้ประเภทความกว้างคงที่แทนประเภทความกว้างต่ำสุด uint32_tฟังก์ชั่นนี้ควรใช้เวลาและการส่งกลับ
Craig Barnes

50

ฉันคิดว่ามันใช้ได้ดีเช่นกัน:

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

powerและคำตอบคือ


19
ยุติธรรมเพียงพอสำหรับคำถามที่ไม่มีการวนซ้ำ แต่ก็ฉลาดพอ ๆ กับฟังก์ชั่นอื่น ๆ สำหรับโค้ดที่ไม่มีประสิทธิภาพที่ละเอียดอ่อนคำตอบที่เข้าใจได้ง่ายและรวดเร็วและตรวจสอบได้ว่าถูกต้องจะชนะสำหรับฉันเสมอ
ทิม MB

2
นี่ไม่ใช่การคืนค่าพลังที่ใกล้ที่สุดของ 2 แต่พลังของมันนั้นใหญ่กว่า X แต่ก็ดีมาก
CoffeDeveloper

1
แทนที่จะใช้การคูณคุณสามารถใช้ "เวทมนต์" bitwise แทนได้power <<= 1
vallentin

5
@Valentin นั้นควรได้รับการปรับให้เหมาะสมโดยอัตโนมัติโดยคอมไพเลอร์
MarkWeston

4
ระวังลูปไม่สิ้นสุดหากxมีขนาดใหญ่เกินไป (เช่นบิตไม่เพียงพอที่จะเป็นตัวแทนของพลังต่อไปของ 2)
alban

36

หากคุณใช้ 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));
}

3
โปรดทราบว่านี่จะส่งคืนพลังงานที่เล็กที่สุดที่ 2 มากกว่าหรือเท่ากับ x การเปลี่ยน (x -1) เป็น x จะเปลี่ยนฟังก์ชั่นเพื่อคืนกำลังที่เล็กลงของ 2 ที่มากกว่า x
Guillaume

2
คุณสามารถใช้_BitScanForwardกับ Visual C ++
KindDragon

นอกจากนี้คุณยังสามารถใช้__builtin_ctz()
MarkP

@MarkP __builtin_ctz()จะไม่เป็นประโยชน์ในการปัดเศษที่ไม่ใช่เลข 2 ใด ๆ ไปจนถึงกำลังสองถัดไป
Yann Droneaud

2
โปรดเพิ่มลิงก์คำตอบของคุณไปยังรายการ Wikipedia ของฟังก์ชั่น                                บิทอินบิวท์อินbuiltin สำหรับคอมไพเลอร์อื่น ๆ : en.wikipedia.org/wiki/Find_first_set#Tool_and_library_supportโปรดให้รุ่น 64 บิตด้วย ฉันเสนอฟังก์ชัน C ++ 11 ต่อไปนี้:              constexpr uint64_t nextPowerOfTwo64 (uint64_t x) { return 1ULL<<(sizeof(uint64_t) * 8 - __builtin_clzll(x)); }
olibre

15

อีกอย่างหนึ่งแม้ว่าฉันจะใช้วงจร แต่ 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;
}

2
ผลลัพธ์ไม่ถูกต้องหากxมีกำลัง 2 ไมโครเพื่อทดสอบว่าจำเป็นต้องมีอินพุตกำลัง 2 #define ISPOW2(x) ((x) > 0 && !((x) & (x-1)))
pgplus1628

@zorksylar มีประสิทธิภาพมากขึ้นจะเป็นif (x == 0) return 1; /* Or 0 (Which is what I use) */ x--; /* Rest of program */
yyny

ทางออกที่ดี! แต่power of two "ceil" optionไม่ถูกต้อง ตัวอย่างเช่นเมื่อx = 2ผลที่ควรจะ2แทน4
MZD

10

สำหรับประเภทที่ไม่ได้ลงนามใด ๆ การสร้าง 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;
}

ไม่มีการวนซ้ำที่แท้จริงเนื่องจากคอมไพเลอร์ทราบเวลารวบรวมจำนวนการวนซ้ำ


4
โปรดทราบว่าคำถามนั้นเกี่ยวกับ C.
martinkunev

@martinkunev เพียงแค่แทนที่ UnsignedType และดำเนินการด้วยตนเอง ฉันค่อนข้างมั่นใจว่าโปรแกรมเมอร์ C สามารถขยายเทมเพลตอย่างง่ายนี้โดยไม่สนใจการstd::is_unsigned<UnsignedType>::valueยืนยัน
user877329

2
@ user877329 แน่นอนว่ามันจะดีถ้ามีคำตอบใน Javascript เช่นกันในกรณีที่บางคนต้องการแปลให้ C.
martinkunev

@martinkunev UnsignedType ใน JavaScript? อย่างไรก็ตามวิธีนี้แสดงให้เห็นว่าจะทำอย่างไรกับ UnsignedType ใด ๆ และมันถูกเขียนขึ้นใน C ++ แทนที่จะเป็น pseudocode [sizeof (v) * CHAR_BIT แทนที่จะเป็นจำนวนบิตในวัตถุของ UnsignedType]
user877329

9

สำหรับ 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 และขึ้นอยู่กับคอมไพเลอร์ของคุณอาจมีตัวประมวลผลที่พร้อมใช้งานสำหรับคุณ


นี่คือหนึ่งที่น่าสนใจแม้ว่าไม่เกี่ยวข้องกับคำถาม (ฉันต้องการเพียง roundoff จำนวนเต็ม) จะลองหนึ่งนี้ ..
Naveen

มาด้วยหลังจากอ่านบทความวิกิพีเดียเกี่ยวกับลอย นอกจากนั้นฉันใช้มันเพื่อคำนวณสแควร์รูทในความแม่นยำจำนวนเต็ม ยังดี แต่ไม่เกี่ยวข้องมากขึ้น
Jasper Bekkers

สิ่งนี้จะทำลายกฎนามแฝงที่เข้มงวด ในคอมไพเลอร์บางตัวอาจไม่ทำงานหรือออกคำเตือน
martinkunev

6

แม้จะมีคำถามถูกติดแท็กเหมือนcที่นี่ห้าเซ็นต์ของฉัน โชคดีที่ C ++ 20 น่าจะรวมstd::ceil2และstd::floor2(ดูที่นี่ ) มันเป็นconsexprฟังก์ชั่นเทมเพลตการใช้GCCในปัจจุบันใช้การเปลี่ยนบิตและทำงานกับประเภทที่ไม่ได้ลงนามใด ๆ


2
พวกเขาเพิ่งเปลี่ยนชื่อเป็นbit_ceil open-std.org/JTC1/SC22/WG21/docs/papers/2020/p1956r1.pdf
Wolfgang Brehm

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
}

หากคุณไม่ต้องการเสี่ยงกับพฤติกรรมที่ไม่ได้กำหนดค่าอินพุตจะต้องอยู่ระหว่าง 1 ถึง 2 ^ 63 แมโครยังมีประโยชน์ในการตั้งค่าคงที่ ณ เวลารวบรวม


นี่อาจเป็นทางออกที่เลวร้ายที่สุด (มันยังขาดคำต่อท้าย ULL ในค่าคงที่ 64 บิต) สิ่งนี้จะสร้างการทดสอบ 32 รายการต่ออินพุตในทุกกรณี ดีกว่าที่จะใช้ในขณะที่มันจะเร็วขึ้นหรือด้วยความเร็วเดียวกัน
xryl669

1
แต่ ... สิ่งนี้สามารถประเมินได้โดยตัวประมวลผลล่วงหน้าหากอินพุตเป็นค่าคงที่และทำให้การดำเนินการเป็นศูนย์ในเวลาทำงาน
Michael

4

เพื่อความสมบูรณ์ที่นี่คือการใช้จุดลอยตัวใน 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);
}

1
เบราว์เซอร์แบบสุ่มถ้าคุณอ่านความคิดเห็นนี้ให้เลือกรหัสนี้ซึ่งเป็นคำตอบที่ดีที่สุดอย่างชัดเจนไม่มีคำแนะนำพิเศษไม่มีการกระตุกนิด ๆ หน่อย ๆ เพียงแค่รหัสที่มีประสิทธิภาพพกพาและมาตรฐาน คาดเดาว่าทำไมไม่มีใครโหวตขึ้นอีกเลย ^^
CoffeDeveloper

5
เบราว์เซอร์แบบสุ่มนี่จะเป็นสิ่งที่ช้ามากหากคุณไม่มีฮาร์ดแวร์จุดลอยตัวพิเศษ ใน x86 คุณสามารถเรียกใช้วงกลมรอบ ๆ รหัสนี้ได้โดยใช้การบิดสองบิต rep bsr ecx,eax; mov eax,0; cmovnz eax,2; shl eax,clเร็วกว่าประมาณ 25 เท่า
Johan

4

โซลูชันเฉพาะของ 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


ข้อผิดพลาดเล็ก ๆ : มันควรกลับ 1 สำหรับ 1 มันไม่ได้
0kcats

ขอบคุณ ในแอพพลิเคชั่นที่ได้รับการพัฒนาเราต้องการ 2 ถึงกำลังไฟแรกอย่างชัดเจนเมื่อ 1 คืออินพุต 1 อาจเป็นกรณีพิเศษที่มีเงื่อนไขโดยไม่ต้องสร้างคำแนะนำเพิ่มเติมอีกมากมายที่ฉันจินตนาการ
NoelC

อัปเดตคำตอบเพื่อรวมเวอร์ชันที่คืนค่า 1 สำหรับค่าอินพุต 1
NoelC

3

ใน 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 คุณสามารถใช้การจับคู่ที่แท้จริง


ไร้ประโยชน์ แต่น่ากลัว!
Marco


0

สถาปัตยกรรมโปรเซสเซอร์หลายตัวรองรับlog base 2หรือดำเนินการคล้ายกันมาก - count leading zeros. คอมไพเลอร์หลายคนมีความเป็นอยู่ที่แท้จริง ดูhttps://en.wikipedia.org/wiki/Find_first_set


มันไม่เกี่ยวกับการหา bit set ที่สูงที่สุด (= bsr) หรือการนับศูนย์นำหน้า เขาต้องการปัดเศษขึ้นเป็นกำลังที่ใกล้เคียงที่สุดของ 2 คำตอบด้วย "ลบ 1 จากนั้นทำ bsr และ shift 1 left" ทำเช่นนั้น
Flo

0

สมมติว่าคุณมีคอมไพเลอร์ที่ดี & มันสามารถทำอะไรนิด ๆ หน่อย ๆ ก่อนที่มือที่เหนือฉันที่จุดนี้ แต่ต่อไปนี้ใช้งานได้ !!!

    // 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

0

ฉันกำลังพยายามที่จะลดกำลังของ 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;
}


0

ตัวแปรของ @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);
}

0

นี่คือสิ่งที่ฉันใช้เพื่อให้นี่เป็นนิพจน์คงที่หากอินพุตเป็นนิพจน์คงที่

#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))

จะลดลงอย่างคงที่


0

คุณอาจพบว่าคำอธิบายต่อไปนี้มีประโยชน์ต่อวัตถุประสงค์ของคุณ:


0

แปลงเป็นทศนิยมแล้วใช้. 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


หมายเหตุคำตอบนี้อยู่ในหลาม
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

หากคุณต้องการสิ่งที่เกี่ยวข้องกับ 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
'สำหรับ' เป็นวง
florin

1
เหรียญเงิน: มันคือ และมันถูกใช้เป็นวงวนที่นี่ใช่ไหม?
Tamas Czinege

9
DrJokepu - ฉันคิดว่า florin ตั้งใจจะพูดที่นี่ว่า OP ขอวิธีแก้ปัญหาแบบวนซ้ำ
Eli Bendersky

-1

ถ้าคุณต้องการหนึ่งบรรทัดแม่แบบ นี่มันคือ

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)); }

นี่คือพฤติกรรมที่ไม่ได้กำหนดใน C หรือ C ++ และจะนำไปสู่ข้อผิดพลาด การแก้ไขnหลายครั้งโดยไม่มีจุดลำดับไม่ถูกต้อง คุณเขียนราวกับว่าn-=1ควรจะเกิดขึ้นก่อน แต่การรับประกันเพียงอย่างเดียวที่นี่คือที่nมีค่าใหม่หลังจากที่;และวงเล็บไม่เปลี่ยนแปลง
sam hocevar

ยิ่งไปกว่านั้นก็ทำให้ตาของฉันมีเลือดออก
Donal Fellows
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.