IEEE-754 Konvertierung

Transistorfips

Level-2
Beiträge
89
Reaktionspunkte
4
Zuviel Werbung?
-> Hier kostenlos registrieren
Auf einer S7-1200 wird eine 32bit Dualzahl (UDINT, Unsigned Double Int) in eine 32bit Realzahl gewandelt so wie es der IEEE-764 Standard erwartet.
Anders auf einer B&R Steuerung.

Beispiel:
Code:
UDINT_Var := 2#1101; //geht auch mit 16#000D
REAL_Var := UDINT_TO_REAL(UDINT_Var);
Resultat im watch Window:
REAL_Var: 13.0
UDINT_Var: 13

Das Ergebnis ist falsch.
Muß man dem Compiler noch irgendetwas mitteilen?
 
Auf einer S7-1200 wird eine 32bit Dualzahl (UDINT, Unsigned Double Int) in eine 32bit Realzahl gewandelt so wie es der IEEE-764 Standard erwartet.
Anders auf einer B&R Steuerung.

Beispiel:
Code:
UDINT_Var := 2#1101; //geht auch mit 16#000D
REAL_Var := UDINT_TO_REAL(UDINT_Var);
Resultat im watch Window:
REAL_Var: 13.0
UDINT_Var: 13

Das Ergebnis ist falsch.
Wie kommst Du drauf, daß da was falsch ist? Was ist wo wie falsch? Oder erkläre eindeutiger was Du meinst.

2#1101 = 16#000D = L#13
Wenn man das mit UDINT_TO_REAL zu REAL wandelt, dann muß das Ergebnis 13.0 sein.

Übrigens heißt der Standard IEEE-754 und nicht 764

Harald
 
IEEE-754 definiert eine32-bit float als Vorzeichen (LSB) + Exp (bit 30..22). + Mantisse (0..21).
Wenn 16#0d = 13.0 dezimal ist, wie soll dann 13.1 dargestellt werden...?

Ich habe eine Zahl (Bitmuster!) die gewissermaßen nach IEEE-754 dargestellt(!) werden muß. Für die Zahl 13.0 sähe das Bitmuster so aus: 16#41500000
Hier gibt's nen Umrechner: https://www.h-schmidt.net/FloatConverter/IEEE754.html

Ich müßte quasi einen Zeiger auf die Zahl legen um sie als 32bit IEEE-754 darzustellen. Leider weiß ich aber noch nicht wie man das macht...
Kann mir jemand einen Stolperstein in die richtige Richtung legen?

Hintergrund ist ein Modbus-Teilnehmer der Reals sendet und diese zum wegschicken halbiert weil ein Modbus-Register bekanntermaßen nur 16bit hat.
 
Du denkst zu kompliziert.
Das GUI zeigt dir immer "lesbare" Zahlen an - wenn der Datentyp eine Zahl ist.
Ein float/real wird dir also nicht als Bitmuster angezeigt sondern für die Anzeige konvertiert in einen lesbare Zahl.
Kein Mensch ohne Excel/Converter schließt von dem Bitmuster 16#41500000 auf einen Zahlenwert 13.

Guga
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Wenn 16#0d = 13.0 dezimal ist, wie soll dann 13.1 dargestellt werden...?
(...)
Hintergrund ist ein Modbus-Teilnehmer der Reals sendet und diese zum wegschicken halbiert weil ein Modbus-Register bekanntermaßen nur 16bit hat.
16#0d ist NICHT 13.0 !

16#0000000D = L#13 = 13 dezimal als Ganzzahl
16#41500000 = REAL#13.0 = 13.0 als Gleitpunktzahl

UDINT_TO_REAL() macht die Umwandlung von 13 Ganzzahl zu 13.0 Gleitpunktzahl (16#0000000D --> 16#41500000)

Du brauchst aber vermutlich gar keine Konvertierung, sondern musst den REAL-Wert beim Modbus-Empfänger einfach nur wieder zusammensetzen.

Das Übertragungsprotokoll Modbus kennt keinen FLOAT/REAL Datentyp, sondern nur 16-Bit Register. Ein 32-Bit REAL-Wert liegt da üblicherweise in 2 aufeinanderfolgenden Registern und kann durch zusammenhängendes Lesen der 2 Register am Stück übertragen werden und beim Empfänger werden die 2x 16-Bit Register = 4 Byte zu 32-Bit zusammengefasst. Im einfachsten Fall bei Siemens nimmt man einfach die 4 aufeinanderfolgenden 8-Bit Bytes und packt sie in ein 32-Bit DWORD oder gleich direkt in eine REAL-Variable. (Je nach Endianness der Empfänger CPU muss dabei H-Word und L-Word getauscht werden.)

Du könntest dem Modbus-Lese-Baustein direkt die Adresse Deiner REAL-Variable übergeben (die 4 Bytes direkt als REAL deklarieren) und den Auftrag, die 2 Register des REAL-Wertes in diese Adresse zu lesen (ich weiß jetzt nicht, ob der Compiler das so zuläßt). Oder Du liest die 2 Register in einen 2-WORD-Empfangspuffer oder in einen 4-BYTE-Empfangspuffer und setzt daraus ein 32-Bit-DWORD zusammen (per AT oder einzeln zusammenbasteln). Zum Schluß muß das entstandene 32-Bit-Bitmuster OHNE Konvertierung direkt in eine REAL-Variable kopiert werden: myRealVar := DWORD_TO_REAL(dwordWert);

Bei folgender Deklaration mit den AT-Sichten hast Du 4 Variablen mit verschiedenen Datentypen auf der selben Adresse liegen und kannst das 32-Bit-Bitmuster nach Belieben herauslesen und in die REAL-Variable kopieren (wenn die Endianness passt):
Code:
ByteBuffer : ARRAY [0..3] OF BYTE;                 //die 4 empfangenen Bytes
WordBuffer AT ByteBuffer : ARRAY [0..1] OF WORD;   //die ursprünglichen 2 Modbus-Register
dwordVar AT ByteBuffer : DWORD;                    //das 32-Bit-Bitmuster
realVar AT ByteBuffer : REAL;                      //das 32-Bit-Bitmuster als REAL-Wert interpretiert

myRealVar : REAL;

Falls immer noch Unklarheit besteht, wie der Modbusteilnehmer den Gleitpunktwert codiert und verschickt, dann nenne uns mal um welches Gerät es sich handelt, am besten mit Link zum Handbuch.

Harald
 
Ich frage auch, wie hast du es in S7-1200 gemacht wo es scheint zu funktionieren ?
Es sollte unmöglich sein ein nach die Konvertierung von INT oder DINT nach REAL das es gibt ein Bruchteil.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Auf einer S7-1200 wird eine 32bit Dualzahl (UDINT, Unsigned Double Int) in eine 32bit Realzahl gewandelt so wie es der IEEE-764 Standard erwartet.
Anders auf einer B&R Steuerung.
Ich frage auch, wie hast du es in S7-1200 gemacht wo es scheint zu funktionieren ?
Arrrgh...
Für welche SPS brauchst Du den Code, um den per Modbus empfangenen Wert in eine REAL-Variable zu speichern? Für die S7-1200, oder die B&R, oder ...?
Mit welcher Software programmierst Du diese SPS?

Wenn die empfangende SPS mit einem Codesys-Derivat programmiert wird, dann spielt wahrscheinlich die Endianness eine Rolle, da müssen die Bytes/Words in die richtige Reihenfolge gebracht werden, und "AT" von Variablen gibt es da so nicht (höchstens vielleicht UNION), und ein beliebiges Bitmuster ohne Konvertierung direkt in eine REAL-Variable speichern geht da nur per Pointer oder UNION.

Harald
 
IEEE-754 definiert eine32-bit float als Vorzeichen (LSB) + Exp (bit 30..22). + Mantisse (0..21).
Das stimmt doch vorn und hinten und auch mittendrin nicht!
Bit 31: Wenn schon, dann MSB (der [D]INT-Zahl) als Vorzeichen. Most significant, nicht least significant.
Bit 30..23: Exponent
Bit 22..0: Mantisse ohne das höchstwertige 1-Bit der (positiven) Mantisse. Das höchstwertige 1-Bit der Mantisse erscheint nirgends, da es immer 1 ist und der Rest ist entsprechend geschoben (Ausnahme: Mantisse = 0).
 
Zuletzt bearbeitet:
Hallo Harald,
vielen Dank für deine ausführliche Antwort (und den andern natürlich auch).

Zum Schluß muß das entstandene 32-Bit-Bitmuster OHNE Konvertierung direkt in eine REAL-Variable kopiert werden: myRealVar := DWORD_TO_REAL(dwordWert);
Genau das ist aber das Problem. Es ist doch schon ein Real-Wert! Folglich darf er NICHT als DWORD nach Real konvertiert werden sondern muß als solcher DIREKT interpretiert werden. Deswegen auch die Idee mit dem Zeiger...


Code:
VAR
    n : USINT;
    Master : Master_typ; (*Variable with the Master_typ struct*)
    num32bit : DWORD;
    ACVolt : REAL;  
    Int_var : UINT;
END_VAR


    num32bit := 16#4364b333; // entspricht 228.7 float nach IEEE-754
    ACVolt := DWORD_TO_REAL(num32bit); // Ergebnis im Watch Window, Ausgabe dezimal: 1.1306729E+09
Das Problem scheint wohl nicht nur bei B&R so zuexistieren. Auf stackoverflow haben mehrere Leutchen das gleiche Problem (auf anderen SPSen) geschildert und sich mit entsprechendem Aufwand ein Workaround in C gestrickt.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Durch ein Missverständnis bezogen sich meine Erklärungen im Detail auf die Programmierumg einer S7-1200.

Also dürfen wir nun davon ausgehen, daß Du eine Lösung für die B&R-Steuerung suchst, und diese mit Codesys oder etwas ähnlichem programmiert wird? (ich kenne B&R nicht)
Dieses Problem hatten wir hier schon öfters (besonders im Zusammenhang mit Modbus), weil in Codesys/TwinCat/... die Zuweisung eines DWORD-Wertes zu einer REAL-Variable ohne Konvertierung nicht möglich ist.

Das Problem kann man mit einem Pointer umgehen. Damit der ST-Compiler typmäßig nicht meckert und auch nicht heimlich konvertiert bei der Zuweisung eines DWORD an eine REAL-Variable, muß das ein "POINTER TO REAL" sein. Dem Pointer kann man dann eine beliebige Adresse unterjubeln und aus der Adresse lesen oder zu der Adresse schreiben, und der ST-Compiler glaubt uns daß das Bitmuster ein REAL ist, weil ja der Pointer vereinbarungsgemäß auf den Speicherplatz eines REAL-Wertes zeigt ;)
Code:
VAR
  dwVar : DWORD;
  rVar : REAL;
  pt_REAL : POINTER TO REAL;
END_VAR

dwVar := 16#4364b333;   //Bitmuster von 228.7 in die DWORD-Variable schreiben

pt_REAL := ADR(dwVar);  //die Adresse der DWORD-Variable dem Pointer_to_Real zuweisen
rVar : = pt_REAL^;      //mittels Real-Pointer den Wert aus der DWORD-Variable lesen und einer REAL-Variable zuweisen

pt_REAL^ := 13.0;       //der DWORD-Variable dwVar einen REAL-Wert zuweisen

Vermutlich könntest Du den Modbus-Empfangspuffer auch gleich mit REAL-Variablen strukturiert deklarieren. Oder mit UNION arbeiten, falls es die in Deinem ungenannten Programmiersystem schon gibt. Dann braucht man keine Pointer.

Harald
 
PS: BYTE, WORD, DWORD ... sind eigentlich keine Datentypen sondern lediglich Speicherplatzgrößen. DWORD ist eine Ansammlung (Bitstring) von 32 Bit. Das 32-Bit-Bitmuster kann man (ohne es zu verändern) als DINT, UDINT, TIME, REAL, ... interpretieren. Nur leider setzt Codesys und Co. DWORD = UDINT und daher wird auch DWORD_TO_REAL() als UDINT_TO_REAL() verstanden und will immer von Ganzzahl-Format UDINT zu Gleitpunkt-Format REAL konvertieren, anstatt das Bitmuster unverändert zu übernehmen. Bei Siemens-S7 wird bei DWORD_TO_REAL() das Bitmuster unverändert übernommen und nur bei UDINT_TO_REAL() wird konvertiert.

Harald
 
Code:
#include <bur/plctypes.h>

#ifdef _DEFAULT_INCLUDES
    #include <AsDefault.h>
#endif
_GLOBAL REAL rWert;
_GLOBAL DWORD dwWert,dwWert1;


union numeric {
    REAL rWert;
    DWORD dwWert;
};
union numeric X;


void _INIT ProgramInit(void)
{
    rWert = 13.0;
}


void _CYCLIC ProgramCyclic(void)
{


    X.rWert = rWert;
    dwWert1 = X.dwWert;
}

watch1.JPG

Hier sieht man das DWORD im Watch für die Real Zahl 13.0
 

Anhänge

  • watch.JPG
    watch.JPG
    19,2 KB · Aufrufe: 10
Zuletzt bearbeitet:
Zuviel Werbung?
-> Hier kostenlos registrieren
Dafür in Ansi C / ISO C eine union zu verwenden, ist in der Norm aber "unspecified behaviour" wie ich vor einiger Zeit gelesen habe. Das heißt, ob es so funktioniert musst du in der Dokumentation deines jeweiligen Compilers nachsehen, da es die Norm nicht hergibt.
 
Als Hardware Entwickler programmiere ich keine Anlagen. Sondern erstelle meine Test Programme meistens in C.
In ST muss ich erst sehen, wie die Union auszuführen ist.

Das Prinzip der Union ist doch immer das gleiche. Verschiedene Datentypen im gleichen Speicherbereich darzustellen.
 
Bei C kann es aber wenn das nicht spezifiziert ist theoretisch sein, dass der Compiler z.B. zur Optimierung deinen Code umstrukturiert und dann den Speicher schon für etwas anderes nutzt. Weil eben unspezifiziert ist, dass du aus einer anderen Variable einer union den Wert liest, als den du zuletzt geschrieben hast. Bei C++11 wurde da meine ich etwas weitergehend festgelegt. Ich habe das früher auch schon so gemacht, bis ich das bei einem Codereview mal gesehen habe, dass das bemängelt wurde.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Liest sich dann so:

"When a value is stored in a member of an object of union type, the bytes of the object
representation that do not correspond to that member but do correspond to other members
take unspecified values."
 
UNION gibt es ab Codesys/TwinCat 3 direkt in ST, da braucht man kein c bemühen. Etwa so:
Code:
TYPE VAR32 : UNION
  dwVar : DWORD;
  rVar : REAL;
END_UNION
END_TYPE

VAR
  X : VAR32;
  MyDwWert : DWORD;
  MyRealWert : REAL;
END_VAR

MyDwWert := 16#4364B333;   //Bitmuster von REAL-Wert 228.7

//DWORD auf REAL-Variable kopieren
X.dwVar := MyDwWert;
MyRealWert := X.rVar;

//und REAL auf DWORD-Variable kopieren
X.rVar := MyRealWert;
MyDwWert := X.dwVar;

Harald
 
Das widerspricht aber einer Union.
Ich glaube das ist ein allgemeiner Text. Und in ST wird das ähnlich funktionieren.

Es macht nur Sinn einer Union einer Variable zuzuweisen und die anderen Variablen auszulesen. Ich könnte aber REAL zuweisen und vielleicht noch ein einzens Array von Char[4]. Das ergibt keinen Sinn mehr.

Union Einmal Schreiben und den gewüschnten Datentyp lesen.
Ist besser als über Pointer auf unbekannte Datentypen zuzugreifen.
 
Zurück
Oben