利用匯編語言指令求一個2的非負整數次冪的次數
在求一個2的非負整數次冪的次數的方法比較一文中,我們比較了多種方法,但大多離不開迴圈和條件判斷。按理說這種二進位制操作,應該有更低階的指令才對,通過網路搜尋,我找到了這個指令,即bsr。
在c語言中可以嵌入彙編,但msvc和gcc的語法不同。
1. msvc版本
#include <cstdio> static inline int ilog2_x86(int x) { int retval; __asm { bsr eax, x mov retval, eax } return retval; } /* static inline int ilog2_x86_bad(int x) //這個寫法報錯error C2415: 不正確的運算元型別 { __asm { bsr x, x } return x; } */ int main() { for(int i=0;i<32;i++) printf("%d\n",ilog2_x86(1<<i)); return 0; }
int log2_x86_asm2(int x) { asm ("bsr %0,%0" : "=r" (x) : "r" (x)); return x; } int log2_x86_asm(int x) { int retval; asm ("bsr %1,%0" : "=r" (retval) : "r" (x)); return retval; } int _bsr_int32_(__int32 num) { __int32 count; __asm__( "bsrl %1, %0\n\t" "jnz 1f\n\t" "movl $-1,%0\n\t" "1:" :"=r"(count):"r"(num)); return count; }
兩種語法的比較,參考
把gcc的版本用在前文的測試用例中,結果如下:
D:\>a test case 0 use divide log(2) 199229440,2184ms use switch 199229440,125ms use for 199229440,405ms use std::log2 199229440,1092ms use while 199229440,234ms use map 199229440,219ms use hash map 199229440,421ms use bin search 199229440,140ms use tab32 199229440,125ms use _bsr_int32_ 199229440,93ms use log2_x86_asm 199229440,62ms use log2_x86_asm2 199229440,78ms test case 1 use divide log(2) 335544480,2153ms use switch 335544480,93ms use for 335544480,577ms use std::log2 335544480,1123ms use while 335544480,297ms use map 335544480,219ms use hash map 335544480,344ms use bin search 335544480,124ms use tab32 335544480,141ms use _bsr_int32_ 335544480,93ms use log2_x86_asm 335544480,78ms use log2_x86_asm2 335544480,78ms
彙編指令的方法不出意料是最快的。
在Fast computing of log2 for 64-bit integers - Stack Overflow中提到,gcc還有一個內建函式,__builtin_clzll(x)返回x二進位制表示中前導0的個數。用變數長度減去它,就是從右邊開始的1的位置了,因此,可以
#define LOG2(X) ((unsigned) (8*sizeof (unsigned long long) - __builtin_clzll((X)) - 1))
,文中也提到,對於x86和AMD64 GCC,這個內建函式最終會編譯成bsr指令。
int LOG2(int X) { return((unsigned) (8*sizeof (unsigned long long) - __builtin_clzll((X)) - 1)); } use _bsr_int32_ 335544480,78ms use log2_x86_asm 335544480,78ms use log2_x86_asm2 335544480,63ms use __builtin_clzll 335544480,94ms
在msvc方法的文章中,還提到一種利用float型別求log的方法,雖然看不懂,也列在這裡。
int ilog2(int a) { float x=a; unsigned int ix = (unsigned int&)x; unsigned int exp = (ix >> 23) & 0xFF; int log2 = int(exp) - 127; return log2; }
經測試,與__builtin_clzll(x)的速度差不多。
#include <intrin.h> using namespace std; static inline int log2_bsr_fun(int x) { int retval; _BitScanReverse((unsigned long*)&retval, x); return retval; } #pragma intrinsic(_BitScanReverse) use asm 335544480,110ms use bsr fun 335544480,93ms
以下彙編程式碼證明_BitScanReverse確實呼叫了bsr。
11. static inline int log2_bsr_fun(int x) 12. { 13. int retval; 14. _BitScanReverse((unsigned long*)&retval, x); 15. return retval; 16. } 17. 18. #pragma intrinsic(_BitScanReverse) 19. static inline int log2_bsr_asm(int x) 20. { 21. int retval; 22. __asm { 23. bsr eax, x 24. mov retval, eax 25. } 26. return retval; 27. } ; Function compile flags: /Ogtpy ; COMDAT ?log2_bsr_asm@@YAHH@Z _TEXT SEGMENT _retval$ = -4 ; size = 4 _x$ = 8 ; size = 4 ?log2_bsr_asm@@YAHH@Z PROC ; log2_bsr_asm, COMDAT ; File d:\power2ms2.cpp ; Line 20 push ecx ; Line 23 bsr eax, DWORD PTR _x$[esp] ; Line 24 mov DWORD PTR _retval$[esp+4], eax ; Line 26 mov eax, DWORD PTR _retval$[esp+4] ; Line 27 pop ecx ret 0 ?log2_bsr_asm@@YAHH@Z ENDP ; log2_bsr_asm _TEXT ENDS ; Function compile flags: /Ogtpy ; COMDAT ?log2_bsr_fun@@YAHH@Z _TEXT SEGMENT _retval$ = -4 ; size = 4 _x$ = 8 ; size = 4 ?log2_bsr_fun@@YAHH@Z PROC ; log2_bsr_fun, COMDAT ; File d:\power2ms2.cpp ; Line 12 push ecx ; Line 14 bsr eax, DWORD PTR _x$[esp] mov DWORD PTR _retval$[esp+4], eax ; Line 16 pop ecx ret 0 ?log2_bsr_fun@@YAHH@Z ENDP ; log2_bsr_fun _TEXT ENDS
順便記錄一下從using-gcc-to-produce-readable-assembly學到的gcc檢視彙編程式碼的辦法.
--加上-g選項,彙編程式碼和行號就能對應了 D:\>g++ -c -g power2gc2.cpp D:\>objdump -d -M intel -S power2gc2.o >power2gc2.asm
方法2,用gcc一步生成,但是結果中沒有函式行號
gcc -S -g asm.c -o asm.asm g++ -S -g asm.cpp -o asm1.asm
相關文章
- 別人家的面試題:一個整數是否是“4”的N次冪面試題
- 【C語言】編寫一個函式,將一個數字字串轉換成該字串對應的數字(包括正整數、負整數)。C語言函式字串
- 一個數number的n次冪 python的pow函式Python函式
- 找到一個數最接近的比它大的2的n次冪的程式碼分析
- 計算位數最高達300位的兩個非負整數的乘積,C語言程式設計實現C語言程式設計
- 【c語言】求兩個數中不同的位的個數C語言
- 『左右失衡的4數3次冪恆等式』恆等式
- java求一個整數的最小因子Java
- 求一個整數的二進位制中1的個數
- [LeetCode-231] Power of Two(判斷一個數是不是2的若干次冪)LeetCode
- 計算2的N次冪n 可輸入,n為自然數
- 三層n數3次冪恆等式恆等式
- n數三層4次冪恆等式恆等式
- n數三層5次冪恆等式恆等式
- 編寫程式數一下1到100的所有整數中出現多少次數字9
- Java語言非遞迴求第n個斐波那契數Java遞迴
- 今天寫了一個統計執行sql次數的指令碼SQL指令碼
- 原始碼分析為什麼HashMap的table長度一定是2的整次冪原始碼HashMap
- 1s內控制向某個請求請求的次數
- 給定一個非空整數陣列,除了某個元素只出現一次以外,其餘每個元素均出現兩次。找出那個只出現了一次的元素。陣列
- 利用HashMap統計字串各個字元出現的次數HashMap字串字元
- 輸入一個整數,返回這個整數的位數
- Google面試題 | 不包含連續1的非負整數Go面試題
- C# 輸入一個整數,求質因數C#
- 利用C語言進行常見的數學運算:一元二次方程求根C語言
- 在 C 中引用匯編語言定義的 .globl 變數變數
- C#練習,編寫一個擲篩子100次的程式,並列印出各種點數的出現次數。C#
- 引數為二叉樹和一個整數,求所有和為該整數的路徑二叉樹
- 【c語言】將正數變成對應的負數,將負數變成對應的正數C語言
- 【c語言】判斷一個數是不是2的n次方C語言
- MATLAB求多項式係數及次數Matlab
- C語言:求二元一次方程C語言
- 3069 求n個整數的和
- 利用冪級數展開式求不定式極限
- C語言公式法求一元二次方差的根C語言公式
- 輸出字串中出現次數最多的字元和次數字串字元
- Leetcode刷題之 【最近的請求次數】LeetCode
- 【KMP求字串匹配次數】 hdu 1686KMP字串匹配