Reputation: 75
How do I test for the least significant bit that's unset if I'm given an integer N. My first attempt (gas syntax):
#define N %ecx
#define return %eax
/* prototype:
* int leastSigUnsetBit(unsigned int N)
*/
.text
.global leastSigUnsetBit
leastSigUnsetBit:
movl N, 4(%esp)
movl $-1, return
Loop:
inc return
bt return, N
jc Loop
ret
Upvotes: 0
Views: 1912
Reputation: 76577
You can test the lsb set using bsf
(bit scan forward) or even better using tzcnt
I hope you are aware of the fact that bsf
will return undefined data when the operand is zero. tzcnt
fixes that.
If you want to test the LSB that's unset, you just invert the input using not
and then test for the LSB
set.
Note that tzcnt
is both (nearly) reverse compatible and faster than bsf
. tzcnt
will be read by an old cpu as rep bsf reg,reg
, however tzcnt modifies the flags differently from bsf
:
bsf: ZF=0 -> input was 0.
tzcnt: ZF=0 -> output is 0, CF=0 -> input was zero.
The best solution is to use tzcnt
, but make sure you've tested that the cpu supports tzcnt
(or it will execute a bsf
and your code will give incorrect results).
;Intel syntax
mov eax,not(-1)
tzcnt edx,eax ;edx = 32
mov eax,[random]
not eax
tzcnt eax,eax
The other alternative is to test whether the operand is zero and adjust accordingly.
bsf
does alter the flags correctly, you can use that to compensate.
From: http://www.felixcloutier.com/x86/BSF.html
BSF ..... Flags Affected
The ZF flag is set to 1 if all the source operand is 0;
not eax
mov edx,32
bsf eax,eax
cmovz eax,edx //force result to 32 if zero inputted.
Note that as a matter of principle I don't write AT&T syntax, you'll have to reverse the operands where applicable.
tzcnt
runs a lot faster than bsf
(because the cpu does not it does not have to do undefined behaviour emulation).
Upvotes: 3