Page 2 of 3

Re: BitGet/BitSet/BitOff

Posted: Wed Feb 10, 2016 12:24 am
by Lunasole
idle wrote:It's probably better to use macros with the assembly instructions BTS, BTR, BTC and a procedure for BT
Thanks for interesting example, but your code cannot be used like that

Code: Select all

Define.i A, B
BitSet (A, 2)
B = BitSet(A, 1) ; B will have 1 and 2 bits set, A only 2nd
Or used inside other expressions, with IF or some else:

Code: Select all

Debug BitSet (0, 1) | BitSet (0, 2); = 3
This is important thing and one of reasons why my macro made like they are. For my macro the only limit is that they cannot be surrounded by some high-priority operators (~,<<,>>,%,!), until code inside of BitSet/BitOff is not taken to ( ).

Also using procedure call for BitGet looks expensive against inline bit operations, as there are arguments pushed, local variables assigned and returned (but in my get macro CBool is called, though, cause using "~~" sequence unfortunately has some minuses).

Re: BitGet/BitSet/BitOff

Posted: Wed Feb 10, 2016 12:27 am
by Lunasole
@Idle

Also don't know why, but your BitSet macro constantly shows 3x slower execution time than my variant on my AMD with that code running w/o debugger:

Code: Select all

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
EnableExplicit

Macro BitSet(bitfield,bit) 
  EnableASM
  bts bitfield , bit 
  DisableASM 
EndMacro 

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Macro BitSetA (Value, Bit)
	Value | (1 << (Bit - 1)) ; 
EndMacro

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Define A
Define B
Define Time
Define Count = 100000000 ; 100 billions iterations


Time = ElapsedMilliseconds()
For B = 1 To Count
	A = 0
	;BitSet(A, 0)
	BitSetA(A, 1)
Next B

MessageRequester("BitSet:ASM", Str(ElapsedMilliseconds() - Time))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


Time = ElapsedMilliseconds()
For B = 1 To Count
	A = 0
	;BitSetA(A, 1)
	BitSet(A, 0)
Next B

MessageRequester("BitSet", Str(ElapsedMilliseconds() - Time))
I guess other macro too, didn't tested. This is strange enough.

Re: BitGet/BitSet/BitOff

Posted: Wed Feb 10, 2016 3:39 am
by idle
I don't notice any difference between them, the latency of BTS is 8 and "shift and or" should add up to 7 or 8
depending how it's done.

Re: BitGet/BitSet/BitOff

Posted: Wed Feb 10, 2016 8:02 am
by wilbert
idle wrote:I don't notice any difference between them, the latency of BTS is 8 and "shift and or" should add up to 7 or 8
depending how it's done.
PureBasic probably replaces the entire (1 << (Bit - 1)) part with a constant so the difference would be like
bts [v_A], 0 vs or [v_A], 1 and the or instruction is faster.

Re: BitGet/BitSet/BitOff

Posted: Wed Feb 10, 2016 5:06 pm
by Little John
Keya wrote:

Code: Select all

Macro BitGet(bitnum, value=0)
  (value & (1<<bitnum))
EndMacro

Macro BitSet(bitnum, value=0)
  (value | (1<<bitnum))
EndMacro

Macro BitUnset(bitnum, value=0)
  (value ! (1<<bitnum))
EndMacro
Hello Keya,

it occurred to me that there is probably a mistake in your code.
According to my limited knowledge of the English language, I think the name "BitUnset" shall denote that the macro sets the wanted bit to 0, right?
But that macro uses XOR, and so the regarding bit is toggled.

I suggest to write the macros as follows:

Code: Select all

Macro BitGet(value, bitnum)
   (value & (1<<bitnum))
EndMacro

Macro BitSet(value, bitnum)
   (value | (1<<bitnum))
EndMacro

Macro BitToggle(value, bitnum)
   (value ! (1<<bitnum))
EndMacro

Macro BitUnset(value, bitnum)
   (value & ~(1<<bitnum))
EndMacro


; -- Demo
a = BitSet(0, 16)
If BitGet(a, 16)   
   Debug "on: " + Bin(a, #PB_Long)
EndIf   

a = BitUnset(a, 16)
If Not BitGet(a, 16)   
   Debug "off: " + Bin(a, #PB_Long) 
EndIf   

a = BitToggle(a, 16)
Debug "toggle on: " + Bin(a, #PB_Long) 
a = BitToggle(a, 16)
Debug "toggle off: " + Bin(a, #PB_Long)

Re: BitGet/BitSet/BitOff

Posted: Wed Feb 10, 2016 6:30 pm
by Lunasole
wilbert wrote:
idle wrote:I don't notice any difference between them, the latency of BTS is 8 and "shift and or" should add up to 7 or 8
depending how it's done.
PureBasic probably replaces the entire (1 << (Bit - 1)) part with a constant so the difference would be like
bts [v_A], 0 vs or [v_A], 1 and the or instruction is faster.
Yes, I posted example of this previously.
But anyway my variant of macro looks as MOV, OR, MOV, 3 instructions against one BTS. It seems that BTS itself is slower, at least on my AMD.

Re: BitGet/BitSet/BitOff

Posted: Wed Jun 22, 2016 4:54 am
by Blue
idle wrote:

Code: Select all

[...]
Global mybit.l 

BitSet(mybit,16) 
If BitGet(mybit,16)    
  Debug "on: " + Bin(mybit,#PB_Long) 
EndIf   

BitReset(mybit,16) 
If Not BitGet(mybit,16)    
  Debug "off: " + Bin(mybit,#PB_Long)  
EndIf   

BitToggle(mybit,16) 
Debug "toggle on: " + Bin(mybit,#PB_Long)  
BitToggle(mybit,16) 
Debug "toggle off: " + Bin(mybit,#PB_Long) 
[...]
If, working from your example, i use a variable instead of a literal value to specify which bit to modify, as in the following

Code: Select all

  [...]

  Global mybit.l 
  define b

  b = 16
  BitSet(mybit, b) 
  If BitGet(mybit,b)    
     Debug "on: " + Bin(mybit,#PB_Long) 
   EndIf   

  [...]
I get this error message (which I totally don't understand ! :cry: ).
     ---------------------------
  PureBasic - Assembler error
     ---------------------------
  PureBasic.asm [135]:
  bts dword [v_mybit],dword [v_b]
  error: invalid operand.
     ---------------------------
                   OK

I guess it has to do with the bug Little John pointed out.
So how would i solve that ?
You mention "aliasing the registers", but how is that done ?

(using PB 5.42 x86 in Windows 10 x64)

Re: BitGet/BitSet/BitOff

Posted: Wed Jun 22, 2016 5:35 am
by Blue
@LunaSole : I like your macros very much.
Very good idea, + excellent and simple implementation.
Bravo !

However, I'll use them by counting up from 0.
As Little John suggested, i think it's better to stick to some standards sometimes.

I'd also like to point out that, while you explain that 'Value' is bitmask, it is actually a bitfield.
A bitmask is a pattern used to filter out or isolate specific bits in a bitfield.

Thank you for your very useful macros.

--------------------------------------------------
Addendum :

I think there is a mistake in your BitOFF() macro.
You are using an XOR operation to turn the bit OFF,
which works if the bit is ON, but fails if it is already OFF ! :shock:

Try this out.

Code: Select all

; using Lunasole's irregular 1-based position counting scheme
Macro BitOFF (Value, bit)
  Value ! (1 << (bit-1))    ;; a zero bit will be turned on with this !
EndMacro

A = 15
Debug "A = " + Bin(A, #PB_Long)
A = BitOFF(A,1)
Debug " BitOFF(A,1)  A = " + Bin(A, #PB_Long)

Debug ""
Debug "That bit #1 went from ON to OFF. OK."
Debug "Even though it's OFF, let's turn it OFF once more :"
Debug ""

A = BitOFF(A,1)
Debug " BitOFF(A,1)  A = " + Bin(A, #PB_Long)
Debug ""
Debug "That bit #1 went from OFF to ON."
Debug "Not the expected result..."
Debug ""
Now, INVERTING the bit and then ANDing it against the bitfield works perfectly :

Code: Select all

; using Lunasole's irregular 1-based position counting scheme
Macro BitOFF (Value, bit)
  Value & ~(1 << (bit-1))
EndMacro

A = 15
Debug "A = " + Bin(A, #PB_Long)
A = BitOFF(A,1)
Debug " BitOFF(A,1)  A = " + Bin(A, #PB_Long)

Debug ""
Debug "That bit #1 went from ON to OFF. OK."
Debug "So it's OFF, but let's turn it OFF again anyway :"
Debug ""

A = BitOFF(A,1)
Debug " BitOFF(A,1)  A = " + Bin(A, #PB_Long)
Debug ""
Debug "And bit #1 stayed OFF, as we wanted..."
Debug ""

   
  
    

Re: BitGet/BitSet/BitOff

Posted: Wed Jun 22, 2016 10:05 am
by Lunasole
@Blue, thanks for your response. I've updated first post and also set starting index to 0 at last :)
However 1 anyway looks better in many cases when starting from 0 is not crucial.

Re: BitGet/BitSet/BitOff

Posted: Wed Jun 22, 2016 1:25 pm
by Blue
Lunasole wrote:@Blue, thanks for your response. I've updated first post and also set starting index to 0 at last :)
However 1 anyway looks better in many cases when starting from 0 is not crucial.
I agree with you that indexing items with 1 for the first item of a suite is a lot more natural and logical for regular humans :D
(That's you and me, brother. It, ipso facto, excludes propeller heads like Little John, Idle, and a bunch of others! :lol: )

The insistance on pointing your unorthodox (NOT abnormal) indexing method was not to force you to change, only to draw to it to the attention of those irregular humans called programmers. I certainly didn't want you to feel that you need to apologize in any way, nor even to change it.

Your idea is still top notch, and your macros, very useful and intelligent.
Stick to your guns : you like indexing from 1, just do it (plus the way you implement it is perfect.)

You say that you updated the first post. But what actually needs updating is NOT your indexing approach, but the method used in BitOFF(), since it fails with bits already zeroed. That point is very important, since, most often, programmers do not know in advance whether the bit of interest is off or not.
  
  
  

Re: BitGet/BitSet/BitOff

Posted: Wed Jun 22, 2016 6:58 pm
by Keya
Little John wrote:Hello Keya,
it occurred to me that there is probably a mistake in your code.
According to my limited knowledge of the English language, I think the name "BitUnset" shall denote that the macro sets the wanted bit to 0, right?
But that macro uses XOR, and so the regarding bit is toggled.

I suggest to write the macros as follows:

Code: Select all

Macro BitGet(value, bitnum)
   (value & (1<<bitnum))
EndMacro

Macro BitSet(value, bitnum)
   (value | (1<<bitnum))
EndMacro

Macro BitToggle(value, bitnum)
   (value ! (1<<bitnum))
EndMacro

Macro BitUnset(value, bitnum)
   (value & ~(1<<bitnum))
EndMacro
well spotted and i agree with your changes, thanks! :)

Re: BitGet/BitSet/BitOff

Posted: Wed Jun 22, 2016 9:24 pm
by idle
Blue wrote: If, working from your example, i use a variable instead of a literal value to specify which bit to modify, as in the following
I get this error message (which I totally don't understand ! :cry: ).
     ---------------------------
  PureBasic - Assembler error
     ---------------------------
  PureBasic.asm [135]:
  bts dword [v_mybit],dword [v_b]
  error: invalid operand.
     ---------------------------
                   OK

I guess it has to do with the bug Little John pointed out.
So how would i solve that ?
You mention "aliasing the registers", but how is that done ?

(using PB 5.42 x86 in Windows 10 x64)

Sorry didn't pay attention to the questions asked earlier, the macro's only supported immediate values
you can use this with either an immediate or a variable but only with integer types.

Code: Select all

CompilerIf SizeOf(Integer) = 8
  Macro ecx : rcx : EndMacro 
  Macro eax : rax : EndMacro
CompilerEndIf   

Macro BitSet(bitfield,bit) 
  EnableASM
  mov eax, bit 
  bts bitfield, eax
  !xor eax,eax 
  DisableASM 
EndMacro 

Macro BitReset(bitfield,bit)
  EnableASM 
  mov eax, bit 
  btr bitfield,eax
  !xor eax,eax
  DisableASM 
EndMacro   

Macro BitToggle(bitfield,bit)
  EnableASM 
  mov eax, bit 
  btc bitfield,eax
  !xor eax,eax
  DisableASM 
EndMacro   

Procedure BitGet(bitfield, bit)
  !movzx eax, byte [p.v_bit]
  !bt [p.v_bitfield], eax
  !setc al
  ProcedureReturn 
EndProcedure

Global mybit.i,a.i 

For a = 0 To 16 Step 2  
  BitSet(mybit,a)
  Debug RSet(Bin(mybit,#PB_Long),16,"0")
Next   

For a = 0 To 16 Step 2 
If BitGet(mybit,a)    
  Debug "on: " + Str(a)  
EndIf   
Next 

For a = 0 To 16 Step 2 
BitReset(mybit,a) 
If Not BitGet(mybit,a)    
  Debug Str(a) + " off: " + RSet(Bin(mybit,#PB_Long),16,"0")
EndIf   
Next 

BitToggle(mybit,16) 
Debug "toggle on: " + RSet(Bin(mybit,#PB_Long),16,"0") 
BitToggle(mybit,16) 
Debug "toggle off: " + RSet(Bin(mybit,#PB_Long),16,"0")



Re: BitGet/BitSet/BitOff

Posted: Thu Jun 23, 2016 5:53 am
by wilbert
idle wrote:

Code: Select all

Procedure BitGet(bitfield.i,bit.i)
  EnableASM 
  !mov ecx, [p.v_bit]
  !bt [p.v_bitfield],ecx
  DisableASM  
  !jnb lbitget 
  ProcedureReturn 1 
  !lbitget:
  !xor eax,eax 
  ProcedureReturn 
EndProcedure
There's no need to use branches for this.
The same can be done without a conditional jump.

Code: Select all

Procedure BitGet(bitfield, bit)
  !movzx eax, byte [p.v_bit]
  !bt [p.v_bitfield], eax
  !setc al
  ProcedureReturn 
EndProcedure

Re: BitGet/BitSet/BitOff

Posted: Thu Jun 23, 2016 6:02 am
by idle
wilbert wrote: There's no need to use branches for this.
The same can be done without a conditional jump.

Code: Select all

Procedure BitGet(bitfield, bit)
  !movzx eax, byte [p.v_bit]
  !bt [p.v_bitfield], eax
  !setc al
  ProcedureReturn 
EndProcedure
Yes that's better. thanks!

Re: BitGet/BitSet/BitOff

Posted: Thu Jun 23, 2016 6:20 am
by Lunasole
Blue wrote: The insistance on pointing your unorthodox (NOT abnormal) indexing method was not to force you to change, only to draw to it to the attention of those irregular humans called programmers. I certainly didn't want you to feel that you need to apologize in any way, nor even to change it.

Stick to your guns : you like indexing from 1, just do it (plus the way you implement it is perfect.)
It's all OK man, I'm still using 1-based in my stuff (when it is more handy than 0), only posted here sample starting from 0

Blue wrote: You say that you updated the first post. But what actually needs updating is NOT your indexing approach, but the method used in BitOFF(), since it fails with bits already zeroed.
Funny, yesterday I changed this in my own template but posted another one here without this fix. Well, updated first post-II ^^