Long bug when negative bit is set

Just starting out? Need help? Post your questions and find answers here.
infratec
Always Here
Always Here
Posts: 7779
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Long bug when negative bit is set

Post by infratec »

Code: Select all

Procedure.l Endian(val.l)
  ProcedureReturn ((val & $000000FF) << 24) | ((val & $0000FF00) << 8) | ((val & $00FF0000) >> 8) | ((val & $FF000000) >> 24)
EndProcedure


Procedure.l EndianFix(val.l)
  Protected Result.q
  Result = ((val & $000000FF) << 24) | ((val & $0000FF00) << 8) | ((val & $00FF0000) >> 8) | ((val & $FF000000) >> 24)
  ProcedureReturn Result
EndProcedure


Define Test.l

Test = $12345678
Debug Hex(Test, #PB_Long)
Test = Endian(Test)
Debug Hex(Test, #PB_Long)

Debug ""

Test = $87654321
Debug Hex(Test, #PB_Long)
Test = Endian(Test)
Debug Hex(Test, #PB_Long)

Debug ""

Test = $87654321
Debug Hex(Test, #PB_Long)
Test = EndianFix(Test)
Debug Hex(Test, #PB_Long)
Results in:
12345678
78563412

87654321
FFFFFF87

87654321
21436587
PB 6.30 x86 on WIndows 10 x64 assembler and c backend.

I can not provide a fix for .q because there is no larger variable type then .q :wink:
User avatar
skywalk
Addict
Addict
Posts: 4292
Joined: Wed Dec 23, 2009 10:14 pm
Location: Boston, MA

Re: Long bug when negative bit is set

Post by skywalk »

Ouch!
Any idea when this started?
Was this ok before v630 Final?
The nice thing about standards is there are so many to choose from. ~ Andrew Tanenbaum
infratec
Always Here
Always Here
Posts: 7779
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: Long bug when negative bit is set

Post by infratec »

Ok, found an other way to fix:

Code: Select all

Procedure.l Endian(val.l)
  ProcedureReturn ((val & $000000FF) << 24) | ((val & $0000FF00) << 8) | ((val & $00FF0000) >> 8) | ((val & $FF000000) >> 24)
EndProcedure


Procedure.l EndianFix(val.l)
  Protected Result.q
  Result = ((val & $000000FF) << 24) | ((val & $0000FF00) << 8) | ((val & $00FF0000) >> 8) | ((val & $FF000000) >> 24)
  ProcedureReturn Result
EndProcedure


Procedure.l Endian2(val.l)
	ProcedureReturn $FF & val >> 24 | ($FF & val) << 24 | ($FF & val >> 16) << 8 | ($FF & val >> 8) << 16
EndProcedure



Define Test.l

Test = $12345678
Debug Hex(Test, #PB_Long)
Test = Endian(Test)
Debug Hex(Test, #PB_Long)

Debug ""

Test = $87654321
Debug Hex(Test, #PB_Long)
Test = Endian(Test)
Debug Hex(Test, #PB_Long)

Debug ""

Test = $87654321
Debug Hex(Test, #PB_Long)
Test = EndianFix(Test)
Debug Hex(Test, #PB_Long)

Debug ""

Test = $87654321
Debug Hex(Test, #PB_Long)
Test = Endian2(Test)
Debug Hex(Test, #PB_Long)
But I still think the first version should work too.
I can not always think about compiler internal conversion stuff. :oops:
infratec
Always Here
Always Here
Posts: 7779
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: Long bug when negative bit is set

Post by infratec »

I don't know if this happens in older versions too, but I think so.
User avatar
skywalk
Addict
Addict
Posts: 4292
Joined: Wed Dec 23, 2009 10:14 pm
Location: Boston, MA

Re: Long bug when negative bit is set

Post by skywalk »

Thanks, I'll keep looking but turns out I only had to use byte swapping for word data coming from an instrument.

Code: Select all

  ;          ; Type,   Bytes,  Min,             Max,             C Type
  w.w[0]     ; Word,       2, -32768,           32767,           short
Like you, I prefer to rely on basic compiler operation to be confirmed by Purebasic unit testing prior to beta release.
Fred - Is there a separate thread for suggestions on UNIT tests for Purebasic/Spiderbasic?
I remember you mentioned BUG reports get added to UNIT tests, but maybe we can request tests added even if not a BUG?
The nice thing about standards is there are so many to choose from. ~ Andrew Tanenbaum
freak
PureBasic Team
PureBasic Team
Posts: 5955
Joined: Fri Apr 25, 2003 5:21 pm
Location: Germany

Re: Long bug when negative bit is set

Post by freak »

This is fully correct. No bug.

PB uses arithmetic shift, as mentioned in the documentation: https://www.purebasic.com/documentation ... ables.html

More details: https://en.wikipedia.org/wiki/Arithmetic_shift

If you want to avoid repeating the sign bit with a shift right, mask out the bits AFTER shifting:

Code: Select all

Procedure.l Endian(val.l)
  ProcedureReturn ((val & $000000FF) << 24) | ((val & $0000FF00) << 8) | ((val & $00FF0000) >> 8) | ((val >> 24) & $000000FF)
EndProcedure
quidquid Latine dictum sit altum videtur
SMaag
Enthusiast
Enthusiast
Posts: 365
Joined: Sat Jan 14, 2023 6:55 pm
Location: Bavaria/Germany

Re: Long bug when negative bit is set

Post by SMaag »

As freak said, It's by design not a bug. It's the PB arithmtic shift. That's often a problem!
You have to mask out the the upper Bits after the shift.

But here is a full solution for the BSWAP

Code: Select all

   
  Procedure.q BSWAP64(value.q)
    
    CompilerIf #PB_Compiler_Backend = #PB_Backend_Asm
       
      CompilerIf #PB_Compiler_32Bit
        !lea ecx, [p.v_value]   ; load effective address of value (:= @value)
        !mov edx, dword [ecx]
        !mov eax, dword [ecx +4]
        !bswap edx
        !bswap eax
        ProcedureReturn         ; 64Bit Return use EAX and EDX Register
        
      CompilerElse
        !mov rax, qword [p.v_value]
        !bswap rax
        ProcedureReturn
        
      CompilerEndIf
      
    CompilerElse  ; C-Backend       
        !return __builtin_bswap64(v_value);
     CompilerEndIf  
   EndProcedure
   
 Structure pSwap ; Pointer Structure for swapping
    a.a[0]    ; unsigned Byte-Value
    u.u[0]    ; unsigned WORD-Value
  EndStructure

Procedure.q BSWAP64_PB(value.q) 
  Protected *Swap.pSwap
  *Swap = @value
  Swap *Swap\a[0], *Swap\a[7]
  Swap *Swap\a[1], *Swap\a[6]
  Swap *Swap\a[2], *Swap\a[5]
  Swap *Swap\a[3], *Swap\a[4]
  ProcedureReturn value
EndProcedure

Test = $8877665544332211
Debug Hex(Test, #PB_Quad)
Debug Hex(BSWAP64(Test), #PB_Quad)
Debug Hex(BSWAP64_PB(Test), #PB_Quad)
If you need more Bit-Functions! I solved nearly all that Bit-Stuff for industrial use:

https://github.com/Maagic7/PureBasicFra ... ule_Bit.pb

And here the Macro verison for an universal BSWAP. But there is a problem in C-Backend, because C-Backend needs all
variables in LoCase.

Code: Select all

;{ TODO:  Solve the LoCase var problem in C-Backend
;}
; ===========================================================================

CompilerIf #PB_Compiler_Backend = #PB_Backend_C
    
    ; there is a problem in C-Backend. In C-Backend we have to 
    ; call BSwapVar always with variable name in lower case
    ; if we define L.l and call BSwapVar(L) we get an error
    ; if we call  BSwapVar(l) it works!
    ; it looks like the compiler LoCase all variables but in a Macro not!
    CompilerWarning "Use of Macro BSwapVar(var) in C-Backend: Attention! Pass all variable names in LoCase : BSwapVar(myvar)! BSwapVar(MyVar) produce an Error!"
    
    Macro BSwapVar(var)          
      CompilerSelect SizeOf(var)
        CompilerCase 2
          !v_#var = __builtin_bswap16(v_#var);                   
        CompilerCase 4
          !v_#var = __builtin_bswap32(v_#var);                 
        CompilerCase 8 
          !v_#var = __builtin_bswap64(v_#var);
      CompilerEndSelect        
    EndMacro
  
  CompilerElse    ; ASM-Backend
    
    ; We use the EnableASM command only to load and save the variable
    ; because if we pass directly the 'MOV EAX, var' we have to add v_ for standard code
    ; or p.v_ for variables in a procedure. With the PB buildin ASM it is done by PB.
    Macro BSwapVar(var)
      EnableASM   
      CompilerSelect SizeOf(var)
        CompilerCase 2         
          !XOR EAX, EAX
          MOV AX, var
          !XCHG AL, AH
          MOV var, AX         
  
        CompilerCase 4          
          MOV EAX, var
          !BSWAP EAX
          MOV var, EAX          
                
        CompilerCase 8  ; Quad
          
          CompilerIf #PB_Compiler_Processor = #PB_Processor_x64           
            MOV RAX, var
            !BSWAP RAX
            MOV var, RAX
                     
          CompilerElse   ; ASM x32         
            LEA ECX, var   ; load effective address of var : ECX= @var
            !MOV EDX, DWORD [ECX]
            !MOV EAX, DWORD [ECX +4]
            !BSWAP EDX
            !BSWAP EAX
            !MOV DWORD [ECX], EAX
            !MOV DWORD [ECX +4], EDX 
                        
          CompilerEndIf    
            
      CompilerEndSelect
      DisableASM
    EndMacro
    
  CompilerEndIf
  
User avatar
Michael Vogel
Addict
Addict
Posts: 2839
Joined: Thu Feb 09, 2006 11:27 pm
Contact:

Re: Long bug when negative bit is set

Post by Michael Vogel »

We have to deal with such things here and then - especially when we try to be extra clever... :wink:
...therefore it doesn't hurt (me) very often but then very hard.

One example I had a while ago, the following code was used as a custom callback for a drawing routine. I thought I did a good job to get a fast code:

Code: Select all

#DrawOpaque=$FF000000
Procedure LedMerge(x,y,s,d)

	x=d&#DrawOpaque
	If x>s
		ProcedureReturn x|Calc\ColorTxt
	Else
		ProcedureReturn (s&#DrawOpaque)|Calc\ColorTxt
	EndIf

EndProcedure
Until PB 6 everything the routine above worked fine (32/64) but with PB 6 I've had to change the header line to Procedure.l LedMerge(x,y,s.l,d.l). I was sure everything will still work fine, but the resulting graphic output was wrong.

So I needed to rearrange everything just a little bit (which took a while to do it anyhow):

Code: Select all

Procedure.l LedMerge(x,y,s.l,d.l)

	x=d&#DrawOpaque
	y=s&#DrawOpaque
	If x>y
		ProcedureReturn x|Calc\ColorTxt
	Else
		ProcedureReturn y|Calc\ColorTxt
	EndIf

EndProcedure
User avatar
skywalk
Addict
Addict
Posts: 4292
Joined: Wed Dec 23, 2009 10:14 pm
Location: Boston, MA

Re: Long bug when negative bit is set

Post by skywalk »

Yes, that's why I suggested unit tests or assert myimportantmath == 42;.
Same here, not big burns, but an ouch nonetheless that should be avoided.
The nice thing about standards is there are so many to choose from. ~ Andrew Tanenbaum
Post Reply