Umwandlung von signed Ziffern zu unsigned inkonsistent

Für allgemeine Fragen zur Programmierung mit PureBasic.
Benutzeravatar
Kurzer
Beiträge: 1617
Registriert: 25.04.2006 17:29
Wohnort: Nähe Hamburg

Umwandlung von signed Ziffern zu unsigned inkonsistent

Beitrag von Kurzer »

Hallo,

ich habe es in ähnlicher Form auch schon im englischen Forum geschrieben, die Problematik jetzt aber noch etwas eingegrenzt.
Es geht zum einen um einen offenbaren Fehler und zum anderen habe ich eine generelle Frage.

Purebasic unterstützt nur wenige vorzeichenlose Datentypen. Die, die es unterstützt sind in meine Augen auch nur Krücken, da es sich hier um die Datentypen .a (Ascii), .u (Unicode) und .c (Char). Allesamt keine Datentypen deren Namen auf die Verwendung mit Zahlen hinweisen.

Nutzt man einen vorzeichenbehafteten Datentypen wie z.B. .b (Byte), dann gestalten sich Abfragen wie die folgenden schwer:

Code: Alles auswählen

Define Wert.b = $ff
If $03 > Wert Then ... Mach was
EndIf
Da $ff bei einem vorzeichenbehafteten Datentypen der -1 entspricht, ist $03 größer als $ff (-1) und die Bedingung wird falsch ausgelöst.
Um das zu umgehen, habe ich die Variable bei einem Vergleich bisher immer mit "& $ff" maskiert.

Code: Alles auswählen

Define Wert.b = $ff
If $03 > Wert & $FF Then ...
Das funktioniert gut.

In einem aktuellen Projekt (und nachdem ich auf ein 64 Bit OS umgestiegen bin) sind mir hier Ungereimtheiten aufgefallen. Daher habe ich mir zwecks Prüfung meiner "Ausmaskierungen" folgenden Testcode geschrieben.

Code: Alles auswählen

; Compile this in x86 mode and then in x64 mode

EnableExplicit

Define b.b
Define w.w
Define l.l
Define q.q

b = $ff
w = $ffff
l = $ffffffff
q = $ffffffffffffffff

Debug "Byte: " + Str(b)
Debug "Byte: " + Str(b & $ff)
Debug b & $ff
Debug "----------------------"
Debug "Word: " + Str(w)
Debug "Word: " + Str(w & $ffff)
Debug w & $ffff
Debug "----------------------"
Debug "Long: " + Str(l)
Debug "Long: " + Str(l & $ffffffff)
Debug l & $ffffffff
Debug "----------------------"
Debug "Quad: " + Str(q)
Debug "Quad: " + Str(q & $ffffffffffffffff)
Debug q & $ffffffffffffffff
Der Code gibt also jeweils einen "zu großen" Byte-, Word-, Long- und Quadwert als reine Zahl mit Vorzeichen aus, dann einmal als ausmaskierte, vorzeichenlose Zahl (per Str()-Funktion) und noch einmal als ausmaskierte, vorzeichenlose Zahl direkt per Debug.

Kompiliert man diesen im 64 Bit Modus, dann bekommt man folgendes Ergebnis:

Code: Alles auswählen

[21:35:38] [Debug] Byte: -1
[21:35:38] [Debug] Byte: 255
[21:35:38] [Debug] 255
[21:35:38] [Debug] ----------------------
[21:35:38] [Debug] Word: -1
[21:35:38] [Debug] Word: 65535
[21:35:38] [Debug] 65535
[21:35:38] [Debug] ----------------------
[21:35:38] [Debug] Long: -1
[21:35:38] [Debug] Long: 4294967295
[21:35:38] [Debug] 4294967295
[21:35:38] [Debug] ----------------------
[21:35:38] [Debug] Quad: -1
[21:35:38] [Debug] Quad: -1
[21:35:38] [Debug] -1
Kompiliert man es aber im 32 Bit Modus, dann bekommt man dieses Ergebnis:

Code: Alles auswählen

[21:35:38] [Debug] Byte: -1
[21:35:38] [Debug] Byte: 255
[21:35:38] [Debug] 255
[21:35:38] [Debug] ----------------------
[21:35:38] [Debug] Word: -1
[21:35:38] [Debug] Word: 65535
[21:35:38] [Debug] 65535
[21:35:38] [Debug] ----------------------
[21:35:38] [Debug] Long: -1
[21:35:38] [Debug] Long: 4294967295
[21:35:38] [Debug] -1
[21:35:38] [Debug] ----------------------
[21:35:38] [Debug] Quad: -1
[21:35:38] [Debug] Quad: -1
[21:35:38] [Debug] -1
Man beachte den dritten Debugwert bei "Long:"
Hier scheint beim 32 Bit Compiler also bereits das Ausmaskieren eines Longwertes fehlzuschlagen. Und zwar nur, wenn man diesen Wert direkt für einen Vergleich nutzt. Wird das selbe Konstrukt (l & $ffffffff) an eine PureBasic-Funktion übergeben, hier die Str()-Funktion, dann wird die Ausmaskierung des Vorzeichens korrekt beachtet.

Fred hat in diesem Thread geschrieben, das alle Nummern PB-intern als Quad behandelt werden. Das würde sich mit dem Phänomen decken, dass die Ausmaskierung bei Übergabe an eine PB Funktion funktioniert. Offenbar gilt das jedoch nicht für die direkte Ausgabe per Debug, denn da wird trotz & $ffffffff eine -1 ausgegeben.

Das Problem habe ich erst bemerkt, als ich mein Projekt testweise als 32 Bit Exe habe compilieren lassen. In meinen Augen ist das ein Bug, was denkt ihr?

Da ich später mit großen Nummern hantiere, brauche ich auch eine Lösung für Quads. Das funktioniert ja offenbar weder mit 32 Bit noch mit 64 Bit, da es keinen größeren Datentyp gibt. Hat hierzu schon mal jemand eine Lösung erarbeitet?
"Never run a changing system!" | "Unterhalten sich zwei Alleinunterhalter... Paradox, oder?"
PB 6.02 x64, OS: Win 7 Pro x64 & Win 11 x64, Desktopscaling: 125%, CPU: I7 6500, RAM: 16 GB, GPU: Intel Graphics HD 520
Useralter in 2024: 56 Jahre.
Benutzeravatar
mk-soft
Beiträge: 3701
Registriert: 24.11.2004 13:12
Wohnort: Germany

Re: Umwandlung von signed Ziffern zu unsigned inkonsistent

Beitrag von mk-soft »

Die Debug Ausgabe hat manchmal ein Fehler.
In diesen Fall ist es aber richtig.

Purebasic arbeitet immer Signed. Ausser bei den Type a und w.
Somit wird und Muss ein negativer Wert von long zu Quad auf negativ bleiben. Das macht PB richtig.

Leider gibt es unter PB kein Unsigned Long oder Quad.
Das ist beim rechen kein Problem solange es zu keinen Überlauf kommt (Long 33 Bits, Quad 65 Bits)

Also beim rechnen den Long-Wert einfach als Unsigned betrachten. Nur bei der Ausgabe muss diese beachtet werden.

Code: Alles auswählen

l1.l = 2000000000
l2.l = 1000
l3.l = l1 * 2 + l2
Debug $FFFFFFFF & l3
Leider gibt es keine vergleicher unter PB die Unsigned sind.
Dazu habe ich diesen Code

Code: Alles auswählen

;-TOP
; Kommentar     : 
; Author        : mk-soft
; Second Author : 
; Datei         : UnsignedCompare.pb
; Version       : 1.05
; Erstellt      : 10.03.2009
; Geändert      : 10.03.2017
; 
; Compilermode  :
;
; ***************************************************************************************

CompilerIf #PB_Compiler_Processor = #PB_Processor_x86 

  ; X86 ************************************************************************************
 
  Procedure Above(a.i, b.i)
  
    ; If a > b
    !MOV    eax,dword [p.v_a]
    !CMP    eax,dword [p.v_b]
    !JBE    l_not_above
    ; ProcedureReturn #True
    !MOV    eax,1
    ProcedureReturn
    !l_not_above:
    ; ProcedureReturn #False
    !XOr    eax,eax
    ProcedureReturn
  
  EndProcedure
  
  ; ***************************************************************************************
  
  Procedure AboveEqual(a.i , b.i)
  
    ; If a >= b
    !MOV    eax,dword [p.v_a]
    !CMP    eax,dword [p.v_b]
    !JB     l_not_above_equal
    ; ProcedureReturn #True
    !MOV    eax,1
    ProcedureReturn
    !l_not_above_equal:
    ; ProcedureReturn #False
    !XOR    eax,eax
    ProcedureReturn
  DisableASM
    
  EndProcedure
  
  ; ***************************************************************************************
  
  Procedure Below(a.i, b.i)
  
    ; If a < b
    !MOV    eax,dword [p.v_a]
    !CMP    eax,dword [p.v_b]
    !JAE    l_not_below
    ; ProcedureReturn #True
    !MOV    eax,1
    ProcedureReturn
    !l_not_below:
    ; ProcedureReturn #False
    !XOr    eax,eax
    ProcedureReturn
    
  EndProcedure
  
  ; ***************************************************************************************
  
  Procedure BelowEqual(a.i, b.i)
  
    ; If a <= b
    !MOV    eax,dword [p.v_a]
    !CMP    eax,dword [p.v_b]
    !JA     l_not_below_equal
    ; ProcedureReturn #True
    !MOV    eax,1
    ProcedureReturn
    !l_not_below_equal:
    ; ProcedureReturn #False
    !XOr    eax,eax
    ProcedureReturn
  DisableASM
    
  EndProcedure

CompilerElse 

 ; X64 ************************************************************************************
 
  Procedure Above(a.q, b.q)

    ; If a >= b
    !MOV    r15,qword [p.v_a]
    !CMP    r15,qword [p.v_b]
    !JBE    l_not_above
    ; ProcedureReturn #True
    !MOV    rax,1
    ProcedureReturn
    !l_not_above:
    ; ProcedureReturn #False
    !XOr    rax,rax
    ProcedureReturn
  
  EndProcedure
  
  ; ***************************************************************************************
  
  Procedure AboveEqual(a.i , b.i)
  
    ; If a >= b
    !MOV    r15,qword [p.v_a]
    !CMP    r15,qword [p.v_b]
    !JB     l_not_above_equal
    ; ProcedureReturn #True
    !MOV    rax,1
    ProcedureReturn
    !l_not_above_equal:
    ; ProcedureReturn #False
    !XOr    rax,rax
    ProcedureReturn
    
  EndProcedure
  
  ; ***************************************************************************************
  
  Procedure Below(a.i, b.i)
  
    ; If a < b
    !MOV    r15,qword [p.v_a]
    !CMP    r15,qword [p.v_b]
    !JAE    l_not_below
    ; ProcedureReturn #True
    !MOV    rax,1
    ProcedureReturn
    !l_not_below:
    ; ProcedureReturn #False
    !XOr    rax,rax
    ProcedureReturn
    
  EndProcedure
  
  ; ***************************************************************************************
  
  Procedure BelowEqual(a.i, b.i)
  
    ; If a <= b
    !MOV    r15,qword [p.v_a]
    !CMP    r15,qword [p.v_b]
    !JA     l_not_below_equal
    ; ProcedureReturn #True
    !MOV    rax,1
    ProcedureReturn
    !l_not_below_equal:
    ; ProcedureReturn #False
    !XOr    rax,rax
    ProcedureReturn
    
  EndProcedure
  
  ; ***************************************************************************************

CompilerEndIf
; test

c = Above(1,2)

Debug "Unsigned grösser"
Debug Above( -1, 1)
Debug Above( 1, 1)

Debug "Unsigned grösser gleich"
Debug AboveEqual( -1, 1)
Debug AboveEqual( 1, 1)

Debug "Unsigned kleiner"
Debug Below( 1, -1)
Debug Below( 1, 1)

Debug "Unsigned kleiner gleich"
Debug BelowEqual( 1, -1)
Debug BelowEqual( 1, 1)

Ausserdem noch diverse Funktionen zur Bitbearbeitung:
Link http://www.purebasic.fr/english/viewtop ... 12&t=66891

P.S. Hier noch eine keine Hilfsfunktion

Code: Alles auswählen

Procedure __Unsigned(Value, Type)
  Select Type
    Case #PB_Byte
      ProcedureReturn (Value & $FF)
      Debug r1
    Case #PB_Word
      ProcedureReturn (Value & $FFFF)
    Case #PB_Long
      ProcedureReturn (Value & $FFFFFFFF)
    Default
      ProcedureReturn Value
  EndSelect
EndProcedure

Macro Unsigned(Value)
  __Unsigned(Value, TypeOf(Value))
EndMacro

b.b = -1
w.w = -1
l.l = -1
r1.i = Unsigned(b)
Debug r1
r1.i = Unsigned(w)
Debug r1
r1.i = Unsigned(l)
Debug r1
Alles ist möglich, fragt sich nur wie...
Projekte ThreadToGUI / EventDesigner V3 / OOP-BaseClass-Modul
Downloads auf MyWebspace / OneDrive
Benutzeravatar
HeX0R
Beiträge: 2959
Registriert: 10.09.2004 09:59
Computerausstattung: AMD Ryzen 7 5800X
96Gig Ram
NVIDIA GEFORCE RTX 3060TI/8Gig
Win10 64Bit
G19 Tastatur
2x 24" + 1x27" Monitore
Glorious O Wireless Maus
PB 3.x-PB 6.x
Oculus Quest 2
Kontaktdaten:

Re: Umwandlung von signed Ziffern zu unsigned inkonsistent

Beitrag von HeX0R »

Diese Hilfsfunktion funktioniert aber nur unter x64!
Besser auf quad umsteigen, dann geht es immer

Code: Alles auswählen

Procedure.q __Unsigned(Value, Type)
  Select Type
    Case #PB_Byte
      ProcedureReturn (Value & $FF)
      Debug r1
    Case #PB_Word
      ProcedureReturn (Value & $FFFF)
    Case #PB_Long
      ProcedureReturn (Value & $FFFFFFFF)
    Default
      ProcedureReturn Value
  EndSelect
EndProcedure

Macro Unsigned(Value)
  __Unsigned(Value, TypeOf(Value))
EndMacro

b.b = -1
w.w = -1
l.l = -1
r1.q = Unsigned(b)
Debug r1
r1.q = Unsigned(w)
Debug r1
r1.q = Unsigned(l)
Debug r1
Benutzeravatar
Kurzer
Beiträge: 1617
Registriert: 25.04.2006 17:29
Wohnort: Nähe Hamburg

Re: Umwandlung von signed Ziffern zu unsigned inkonsistent

Beitrag von Kurzer »

:allright: Vielen Dank euch beiden.
Das Unsigned Macro ist schon mal super als Workaround.
"Never run a changing system!" | "Unterhalten sich zwei Alleinunterhalter... Paradox, oder?"
PB 6.02 x64, OS: Win 7 Pro x64 & Win 11 x64, Desktopscaling: 125%, CPU: I7 6500, RAM: 16 GB, GPU: Intel Graphics HD 520
Useralter in 2024: 56 Jahre.
Antworten