TIA genau ein Bit true

MFreiberger

Level-3
Beiträge
3.283
Reaktionspunkte
926
Zuviel Werbung?
-> Hier kostenlos registrieren
Moin,

gibt es eine elegante Möglichkeit (ohne Schleife), herauszufinden, ob in einer Variablen genau ein Bit true ist?

Das ist meine Variante mit Schleife:

Code:
// Variable auswerten
// 
#Tdw_mask := dw#16#1; // erste Bit der Maske setzen

FOR #i := 0 TO #Ti_cnt DO // #Ti_cnt bei 32bit-Variable = 31
    IF
        (#Tdw_value AND #Tdw_mask) <> 0 // wenn Maske <> 0, dann ist mind. 1 Bit true
        AND (#Tdw_value AND (#Tdw_mask XOR dw#16#ffff_ffff)) = 0 // wenn invertierte Maske = 0, dann ist kein weiteres Bit true
    THEN
        #Tx_result := true;
        EXIT;
    ELSE
        #Tx_result := false;
    END_IF;
    #Tdw_mask := SHL_DWORD(IN := #Tdw_mask, N := 1); // Maske inkrementieren
END_FOR;

Aber vielleicht gibt es noch eine kürzere Möglichkeit?

VG

MFreiberger
 
gibt es eine elegante Möglichkeit (ohne Schleife), herauszufinden, ob in einer Variablen genau ein Bit true ist?
Ja, gibt es. Die Aufgabenstellung kann man auch so formulieren: "Prüfen ob der Wert in der Variable eine Potenz von 2 ist", dann findet man evtl. mehr im Internet.

Hier hatte ich das mal für Codesys-ST implementiert:
schauen, ob der Wert eine Potenz von 2 ist (dann ist genau 1 Bit gesetzt)
Bit Twiddling Hacks: Determining if an integer is a power of 2
(...)

(C) Wenn Du auf genau 1 Bit gesetzt prüfen willst, dann mußt Du noch den Spezialfall wTest = 0 behandeln, z.B.
Code:
[COLOR="#008000"]//xResult ist true wenn genau 1 Bit gesetzt ist[/COLOR]

IF wTest = 0 THEN
  xResult := 0;
ELSE
  xResult := (wTest AND -wTest) = wTest;
END_IF;

[COLOR="#008000"]//oder ohne IF[/COLOR]
xResult := wTest <> 0 AND ((wTest AND -wTest) = wTest);
Der Code dürfte so ungefähr auch in TIA SCL funktionieren. Irgendwo hier im Forum muß auch eine Variante für SCL (oder AWL?) rumliegen (finde ich aber gerade nicht so schnell).

Harald
 
Das AWL-Original von Thomas_v2.1 bzw. Deine noch kürzere Variante direkt darunter.
Ja, das war auch ein schöner cleverer Algorithmus - für ein ähnliches Standard-Problem.
Achtung, der verlinkte Code ermittelt nicht, ob genau ein Bit gesetzt ist, sondern ob mehr als ein Bit gesetzt ist. Die Negation davon ist nicht "genau ein Bit gesetzt" sondern "höchstens ein Bit gesetzt". Für "genau ein Bit gesetzt" müsste man noch den Spezialfall "kein Bit gesetzt" (der Eingangswert ist 0) abfangen. Außerdem geht der Algorithmus über REAL/Float nur für 16 Bit, nicht 32 Bit. Ein paar Beiträge weiter im Beitrag #13 findet man dann den hier in #4 verwendeten Algorithmus in AWL, der zunächst das niedrigste gesetzte Bit maskiert/isoliert mittels "wTest AND -wTest" (*), und dann schaut, ob das eine isolierte Bit das einzige Bit war, wenn also das Ergebnis = Eingangswert ist. Leider liefert auch der Eingangswert 0 das Ergebnis 0, so daß dieser Spezialfall für "genau ein Bit gesetzt" noch abgefangen werden muß.
Mit ein bisschen "um die Ecke denken" kann man solche cleveren Zahlenspiele für verschiedene Aufgaben ausbauen. :cool:

(*) Der Ausdruck (v & -v) extrahiert das niedrigstwertige 1-Bit aus v

Harald
 
Moin,

hier eine SCL-Variante für TIA-Portal:

Code:
xResult := (wTest <> 0) AND ((wTest AND (wTest - 1)) = 0);

@Harald: irgendwie komme ich nicht dahinter, warum Du für Codesys-ST statt "= 0" "= wTest" schreibst. Genauso ist mir das -wTest unklar, aber ich denke das liegt in der Eigenart der Codesys-ST-Syntax?

VG

Mario
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Moin,

hier eine SCL-Variante für TIA-Portal:

Code:
xResult := (wTest <> 0) AND ((wTest AND (wTest - 1)) = 0);

@Harald: irgendwie komme ich nicht dahinter, warum Du für Codesys-ST statt "= 0" "= wTest" schreibst. Genauso ist mir das -wTest unklar, aber ich denke das liegt in der Eigenart der Codesys-ST-Syntax?

VG

Mario

In TIA kann ich diese Lösung kompilieren, in Step7 Classic nicht:
1Bit.jpg
 
Moin,

dann umgekehrt: kann der AND-Operator für INT-Variablen verwendet werden?

edit: zu langsam

Dann immer explizit konvertieren?

Gruß

Mario
 
Komischerweise wird anscheinend das Umkehren des Vorzeichens '-wTest' in Haralds Lösung nicht bemeckert. Für meinen Geschmack wäre das auch eine arithmetische Operation, die bei Word nicht zulässig sein dürfte. Hier hätte ich ebenfalls erwartet, dass 2 TypKonvertierungen erforderlich wären. :confused:
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Wenn der ST/SCL-Compiler penibel ist (und die IEC 61131 genau beachtet) dann muß er anmeckern:
- AND ist nur für Bitstring-Datentypen Word/DWord/... erlaubt
- Rechnen wie -x ist nur für numerische Datentypen INT/DINT/... erlaubt

Nun muß man dem Compiler beibringen, daß man weiß was man tut :cool: indem man zwischendurch DINT_TO_DWORD und DWORD_TO_DINT einbaut.
Ich werde es mal für TIA testen.

Harald
 
Wenn der ST/SCL-Compiler penibel ist (und die IEC 61131 genau beachtet) dann muß er anmeckern:
- AND ist nur für Bitstring-Datentypen Word/DWord/... erlaubt

Code:
xResult : BOOL;
wTest : INT;

xResult := (wTest <> 0) AND ((wTest AND (wTest - 1)) = 0);
Komischerweise wird der CODE in TIA ( V15.1, IEC Prüfung aktiviert ) nicht angemeckert :rolleyes::confused:
 
Nun muß man dem Compiler beibringen, daß man weiß was man tut :cool: indem man zwischendurch DINT_TO_DWORD und DWORD_TO_DINT einbaut.

Wenn man diesem Code jetzt auch noch ein paar DINT_TO_WORD usw verpasst, versteht dann später noch jemand was da passiert?
Ich finde den Code eher schlecht lesbar/unverständlich für Instandhalter oder andere Programmierer...

Wäre nicht ein Aufruf von BITSUM mit Auswertung auf =1 verständlicher oder habe ich etwas falsch verstanden.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Moin DMA,

ich stimme Dir zu: wenn man noch (mehrere) explizite Konvertierungen hinzufügen muss, wird es unverständlicher. BITSUM mit einem Ergebnisvergleich auf 1 einzusetzen ist sicherlich eine simple Lösung. Wie Du schon schreibst, meckerst TIA die impliziten Konvertierungen nicht an.
Bei entsprechender Kommentierung halte ich die Möglichkeit dieses Einzeilers aber für akzeptabel.

Ich habe den Code in eine FC verpackt, bei der ich als Eingang einen Variant habe und den Datentyp abfrage. So ist es egal, ob man byte, char, int, word, dint, dword, usint, ... abfragen will. Dazu gibt es dann einen boolschen Rückgabewert.

Also:

Code:
xResult := Chk_oneBitTrue(Iv_value := #irgendEinWert);

VG

MFreiberger
 
Zuletzt bearbeitet:
So wird es von TIA V15.1 nicht angemeckert:
Code:
FUNCTION "IsGenau1Bit" : Bool
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : PN_DP
VERSION : 0.1
   VAR_INPUT 
      IN : DWord;
   END_VAR

BEGIN

// liefert TRUE wenn genau 1 Bit gesetzt ist
#IsGenau1Bit := #IN <> 0 AND ((#IN AND DINT_TO_DWORD(- DWORD_TO_DINT(#IN))) = #IN);
	
END_FUNCTION
Bitte testen.

Bei so einer Function würde ich keine Rücksicht nehmen, ob ein Anwender den Code wegen der expliziten Konvertierungen oder ganz allgemein versteht. Das ist eine "Blackbox", die tut genau das was der Name und der Kommentar sagt. Deswegen total uneffiziente Lösungswege wie z.B. Schleifen suchen? Würde ich nicht machen. Und ich bezweifle auch, das so ein Anwender die BITSUM-Funktion intern versteht - der käme wohl auch kaum auf die Idee da reinzuschauen.

Harald
 
Zurück
Oben