Entwickler-Ecke

Delphi Language (Object-Pascal) / CLX - Aus Gleitkommazahl einzelne Bytes erhalten


daywalker0086 - Mo 12.08.13 16:46
Titel: Aus Gleitkommazahl einzelne Bytes erhalten
Hallo Leute,
ich habe eine Gleitkommazahl z.Bsp. m:=116.0 (Wertebereich 4byte).
Diese soll über die serielle Schnittstelle übertragen werden. Dazu muss ich in diesem Fall den Wert 0x42e80000 übertragen wie hier auch durch umrechnen der Zahl 116.0 als Ergebnis http://gregstoll.dyndns.org/~gregstoll/floattohex/.
Die frage ist nun wie ich an die Werte komme also ich gehe davon aus das im Speicher der Variable m genau di eZeichenfolge steht, nur wie komme ich daran?

Grüße Christian


IhopeonlyReader - Mo 12.08.13 16:52

ich speicher steht das sicher nicht :D
dort stehen die einzelnen Bits...
falls du bit für bit lesen willst, kannst du folgendes machen
(cardinal ist z.B. 4 Byte groß)

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
var X: Cardinal;
C: Byte;
begin
For C:=(8*4)-1 downto 0 do // 1 Byte = 8 bits deshalb 8*4.. -1, da bei 0 begonnen wird zu zählen
begin
if Boolean( X and (1 shl C)) then //bit an Stelle C+1 ist auf 1
else //auf ist 0
end;


daywalker0086 - Mo 12.08.13 17:00

Ich will ja nicht unbedingt bit für bit haben.
Ich habe eine Gleitkommazahl vom typ real, und muss diese Zahl per serielle Schnittstelle übertragen. Der Senderoutine kann ich aber nur einzelne Bytes übergeben. Das heißt Wenn ich eine Real Zahl habe, welche 4 byte groß ist, wie kann ich dann die einzelnen 4 bytes an die serielle Schnittstelle übergeben?


jfheins - Mo 12.08.13 17:04

Aber sicher steht das im Speicher ;-)

Kann deine Senderoutine auch Pointer+Länge? Oder nur Bytearrays?

Besonders einfach ginge es, indem du ein ByteArray mit absolute auf den gleichen Speicher zeigen lässt, wie deine Single-Variable.

Real ist übrigens obsolet. Benutze double oder single stattdessen - in diesem Fall single, da du ja genau vier Bytes haben möchtest.


IhopeonlyReader - Mo 12.08.13 17:08

achso :D

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
var X: Cardinal;
    B: Array[1..4of Byte;
begin
X := $FFFFFFF0;
B[1] := X shr (8*3);  // X shr 24 = $FF
B[2] := X shr (8*2); // X Shr 16 = $FF
B[3] := X shr (8*1); //X Shr 8 = $FF
B[4] := Byte(X and 255); // oder einfach nur Byte(X); = $F0
end;

B[1] enthält die ersten 8 Bit, B[2] die n ächsten 8...


jaenicke - Mo 12.08.13 17:13

user profile icondaywalker0086 hat folgendes geschrieben Zum zitierten Posting springen:
Die frage ist nun wie ich an die Werte komme also ich gehe davon aus das im Speicher der Variable m genau di eZeichenfolge steht, nur wie komme ich daran?
Die Hexadezimaldarstellung interessiert den seriellen Port wenig. Dort musst du die Bytes hinschicken. Die Hexadezimaldarstellung bekommst du z.B. so:

Delphi-Quelltext
1:
2:
3:
4:
5:
var
  a: Single;
begin
  a := 116.0;
  ShowMessage(IntToHex(PInteger(@a)^, 8));
Je nachdem welche Komponente du für den Zugriff auf den seriellen Port benutzt, ist das etwas unterschiedlich, aber normalerweise gibt es dort eine Funktion Write oder so ähnlich zum Schreiben, der du einen Pointer und die Datengröße übergeben kannst. Den Pointer auf die Daten bekommst du mit dem @.


daywalker0086 - Mo 12.08.13 17:34

Also mal eine Rückmeldung

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
var X: Cardinal;
C: Byte;
begin
For C:=(8*4)-1 downto 0 do // 1 Byte = 8 bits deshalb 8*4.. -1, da bei 0 begonnen wird zu zählen
begin
if Boolean( X and (1 shl C)) then //bit an Stelle C+1 ist auf 1
else //auf ist 0
end;

funktioniert nicht da X hier cardinal ist, ich aber ein real Wert habe.


Delphi-Quelltext
1:
2:
3:
4:
5:
var
  a: Single;
begin
  a := 116.0;
  ShowMessage(IntToHex(PInteger(@a)^, 8));


Da steht bei mir im Dialog nur 00000000.

EDIT: Ok zeigt das richtige an, hatte a noch als real deklariert.

Also bis jetzt noch keine Lösung.
Hier mal meine Senderoutine damit ihr seht wie ich Daten über die serielle verschicke:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
    daten[0]:=$06//Adresse der Switchbox im Netzwerk
    daten[1]:=$10//Kommando für Programmierug der SB
    daten[2]:=$06//Länge der Nachricht ohne CRC minus 7
    daten[3]:=wr_anzahl;
    daten[4]:=(sw1); //C3
    daten[5]:=maske_berechnen(1);
    
    for i:=0 to 5 do
    begin
      prog_finish := 0;
      sendstr:=sendstr+inttohex(daten[i],2);
    end;
serialp1.SendString(hextostr(sendstr));

Die Komponente ist SerialNG von DomIS.
http://www.domis.de/cms/index.php?module=ContentExpress&func=display&bid=24&btitle=Content_Menu&mid=3&ceid=7


daywalker0086 - Mo 12.08.13 17:48

So hab es jetzt so gelöst:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
procedure TForm1.Button5Click(Sender: TObject);
var
  a: Single;
  wert:string;
  wert_byte: array[1..4of string;
begin
  a := 116.5;
  wert:= IntToHex(PInteger(@a)^, 2);
  wert_byte[1]:= copy(wert,0,2);
  showmessage(wert_byte[1]);
  wert_byte[2]:=copy(wert,3,2);
  showmessage(wert_byte[2]);
  wert_byte[3]:= copy(wert,5,2);
  showmessage(wert_byte[3]);
  wert_byte[4]:= copy(wert,7,2);
  showmessage(wert_byte[4]);

end;

Jetzt hab ich im Array wert_byte jedes einzelne Byte drin und kann es so über die serielle Schnittstelle ausgeben.

Jetzt die Frage: gehts noch eleganter/einfacher?


MeierZwoo - Mo 12.08.13 18:10

Mich würde grade bei Gleitkommatypen als erstes interessieren, ob die Gegenseite mit den Bytes überhaupt etwas anfangen kann. Auf der Empfängerseite kann ja ein ganz anderes Programm, durch einen völlig anderen Compiler oder auch nur einer anderen Compiler-Version erstellt die Bytes lesen und sich wundern, was das soll.

Denn die Gegenseite muß ja den exakt selben Datentyp verwenden, um die Bytes dann wieder in der richtigen Form in den Speicher zu schreiben ... und zu interpretieren. Und da kann schon ein Versionswechsel, oder eine andere Plattform (32bit, 64bit) alles zunichte machen.

Deshalb ist bei DFÜ der Normalfall, die Werte in Strings umzuwandeln und so zu senden ... es sei denn, es handelt sich auf exakt aufeinander abgestimmte Hardware mit abgestimmter Firm- und Software, wie z.B. im Meß- und Regelwesen.


IhopeonlyReader - Mo 12.08.13 18:41

user profile icondaywalker0086 hat folgendes geschrieben Zum zitierten Posting springen:
Also mal eine Rückmeldung

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
var X: Cardinal;
C: Byte;
begin
For C:=(8*4)-1 downto 0 do // 1 Byte = 8 bits deshalb 8*4.. -1, da bei 0 begonnen wird zu zählen
begin
if Boolean( X and (1 shl C)) then //bit an Stelle C+1 ist auf 1
else //auf ist 0
end;

funktioniert nicht da X hier cardinal ist, ich aber ein real Wert habe.

dein ernst? dann mach halt
X als real und anstatt in der schleife 8*4 schreibste 8*8.. (real ist doch 8 Byte groß oder?)


IhopeonlyReader - Mo 12.08.13 18:46

mal alles auf real umgeschrieben

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
var X: Real;
    B: Array[1..8of Byte;
    C: Byte; //Counter
    HilfX: Real;
begin
//x setzten auf was für einen wert auch immer
For C := 7 downto 0 do
  B[8-C] := X shr (8*C)
end

//und wieder zusammensetzten
X := 0;
For C:=7 downto 0 do
  begin
  HilfX := B[C];
  HilfsX := HilfsX shl (8*C);
  X := X or HilfsX;
  end;


Moderiert von user profile iconMartok: Code- durch Delphi-Tags ersetzt


jaenicke - Mo 12.08.13 20:43

Warum um alles in der Welt friemelst du da mit Strings herum, wenn du gar keinen String schicken willst? Die genannte Komponente hat den Befehl SendData...

Delphi-Quelltext
1:
2:
3:
4:
var
  a: Single;

serialp1.SendData(@a, SizeOf(a));
Und um die Bytes alle zu schicken könntest du ein array of Byte benutzen.


mandras - Mo 12.08.13 21:31

jaenickes Methode ist die kürzeste,

verständlicher ist vielleicht folgendes wenn man doch auf einzelne Bytes zugreifen muß:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
procedure TForm1.Button1Click(Sender: TObject);
var a:single;
    b:array[0..3of byte absolute a;
begin
 a:=116;
 // danach ist:
 // b[0]=0, b[1]=0, b[2]=$e8, b[3]=$42
end;


jasocul - Di 13.08.13 08:18

Und wer noch weiß, was ein Record ist, kann es auch über einen varianten Record machen:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
var
  a : record
    case Boolean of
      True  : (s : Single);
      False : (b : Array[0..3of Byte);
    end;
...
a.s := 116.5;

Dann steht in a.b Byte-Darstellung.

Aber ich würde auch die Variante von Jaenicke nehmen. :wink: