Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Delphi Mal wieder HexToFloat (https://www.delphipraxis.net/193216-mal-wieder-hextofloat.html)

echitna 6. Jul 2017 09:19

Mal wieder HexToFloat
 
Hallo,

ich habe folgendes Problem:
Ich bekomme über eine Schnittstelle einen String, der eine Hexadezimalzahl repräsentiert (4 Zeichen lang), also z.B. 'FF E4' oder '00 03'.
Dieser String muss jetzt in einen Gleitkommawert umgewandelt werden. Ich habe eine Funktion dafür, die den Wert in binärstring
umwandelt so dass dann die Mantisse und der Exponent ausgerechnet werden kann. Die Funktion gibt dann das Ergebnis der Rechnung result := mantisse * power(2,exponent) aus. Das Problem ist jetzt, dass ich mit der Funktion eine normalisierte Zahl (also z.B. 9.17963e-41) als Ergebnis bekomme, ich hätte aber gerne eine 'normale' Kommazahl (also z.B. 0,00000917).
Hat jemand eine Idee wie man das hinkriegt, oder gibt es vielleicht sogar noch eine einfachere Methode einen Hex-String in eine Kommazahl zu konvertieren?

(Tante Google hat mir leider auch nicht geholfen)

gammatester 6. Jul 2017 09:33

AW: Mal wieder HexToFloat
 
Was soll das für ein Format sein? 4 Hex-Zeichen sind 16-Bit, das ist ein Format, das ungeöhnlich ist (single hat 32-Bit). Ohne Dokumentation des internen Aufbaus des 16-Bit-Wortes kann man wohl wenig sagen oder helfen.

himitsu 6. Jul 2017 09:34

AW: Mal wieder HexToFloat
 
Nein, du bekommst eine unformatierten "binären" Wert.

Wie du diesen Wert dann darstellst, also wieder in einen String umwandelst, ist dir überlassen.
z.B. Delphi-Referenz durchsuchenFormat oder Delphi-Referenz durchsuchenFormatFloat

Neutral General 6. Jul 2017 09:38

AW: Mal wieder HexToFloat
 
Du kannst folgendes machen:
Delphi-Quellcode:
function HexToSingle(hex: String): Single;
var tmp: Cardinal;
begin
  tmp := StrToInt('$' + hex);
  Move(tmp, Result, SizeOf(Cardinal));
end;

var f: Single;
begin
  f := HexToSingle('FFE4');
  Caption := FloatToStr(f);
end;
Prinzipiell kannst du mit FloatToStrF oder Format die Zahl (wenn du sie als String ausgeben willst) auf X Nachkommastellen formatieren.
Hinweis: 9.17963e-41 ist NICHT 0,00000917. Die Darstellung "9.17963e-41" wird benutzt, weil die Zahl ausgeschrieben so aussehen würde:

0,0000000000000000000000000000000000000000917963

gammatester 6. Jul 2017 10:08

AW: Mal wieder HexToFloat
 
Zitat:

Zitat von Neutral General (Beitrag 1376104)
Du kannst folgendes machen:
Delphi-Quellcode:
function HexToSingle(hex: String): Single;
var tmp: Cardinal;
begin
  tmp := StrToInt('$' + hex);
  Move(tmp, Result, SizeOf(Cardinal));
end;

Ich glaube nicht, daß das so gewollt ist. Das Ergebnis ist immer ein denormalisiertes Single. Außerdem kann man so keine negativen Zahlen erhalten. Wie wäre es mit
Delphi-Quellcode:
function HexToSingle(hex: String): Single;
var tmp: smallint;
begin
  tmp := StrToInt('$' + hex);
  Result := tmp;
end;

Neutral General 6. Jul 2017 12:07

AW: Mal wieder HexToFloat
 
Zitat:

Zitat von gammatester (Beitrag 1376113)
Wie wäre es mit
Delphi-Quellcode:
function HexToSingle(hex: String): Single;
var tmp: smallint;
begin
  tmp := StrToInt('$' + hex);
  Result := tmp;
end;

Eh ja, so gehen alle Nachkommastellen verloren und die Zahl ist komplett falsch.

gammatester 6. Jul 2017 13:27

AW: Mal wieder HexToFloat
 
Zitat:

Zitat von Neutral General (Beitrag 1376133)
Eh ja, so gehen alle Nachkommastellen verloren und die Zahl ist komplett falsch.

Welche Nachkommastellen? Wie schon gesagt: Solange keine Auskunft über die Bedeutung der 4 Hex-Zeichen gemacht wird (zB: nimm den Integerwert/100), sehe ich erst einmal keine Nachkomnastellen. Deine Nachkommastellen sind doch völlig sinnlos, wenn man bedenkt, daß die kleinste normalisierte Singlezahl ca 1.17549e-38 ist und damit ca 128 mal größer(!) als 9.17963e-41. Und das gilt für jede vierstellige Hexeingabe.

Neutral General 6. Jul 2017 13:35

AW: Mal wieder HexToFloat
 
Vielleicht waren es auch nur schlechte (zu kurze) Beispiele?
Ich habe es so interpretiert dass er/sie "echte" Floats als Hex-String erhält.
Seine/Ihre Vorgehensweise diese umzuwandeln und dass mein Code das gleiche Ergebnis erzielt scheint das zu bestätigen.

Dass die Ergebnisse nicht so aussehen als wären sie gewollt stimmt wohl.
Es ist wahrscheinlich nicht auszuschließen dass er/sie selbst nicht genau weiß diese Hex-Strings darstellen und wie sie decodiert werden müssen.

Von daher: Wir brauchen mehr Infos.

HolgerX 6. Jul 2017 13:45

AW: Mal wieder HexToFloat
 
Hmm

Also ich vermute, dass es sich um HalfFloat oder auch Float16 handelt.
Diese haben nur 2 Bytes und werden in Hex eben so dargestellt.

Jedoch müssen diese in Delphi umgerechnet werden auf z.B. single.

Hierbei wird daraus (glaube ich) zunächst ein WORD (mit StrToInt('$'+AString)) gemacht und dieses dann nach Single konvertiert.

Beispiele habe ich keine...

Neutral General 6. Jul 2017 13:50

AW: Mal wieder HexToFloat
 
Zitat:

Zitat von HolgerX (Beitrag 1376144)
Hierbei wird daraus (glaube ich) zunächst ein WORD (mit StrToInt('$'+AString)) gemacht und dieses dann nach Single konvertiert.

Beispiele habe ich keine...

Das ist was mein Code weiter oben macht. Aber mit Cardinal, statt Word, was aber unterm Strich keinen Unterschied macht.
Das Problem ist, dass ein 16-Bit Float in einem 32-Float nicht den gleichen Wert hat.

HolgerX 6. Jul 2017 13:54

AW: Mal wieder HexToFloat
 
Hmm..

Müsste sich mit dieser beschreibung nicht das WORD Bitweise zerlegen lassen und damit dann die Float richtig erstellt werden, unabhänging ob nun Single oder Double...

https://en.wikipedia.org/wiki/Half-p...g-point_format

Neutral General 6. Jul 2017 14:16

AW: Mal wieder HexToFloat
 
Zitat:

Zitat von HolgerX (Beitrag 1376149)
Hmm..

Müsste sich mit dieser beschreibung nicht das WORD Bitweise zerlegen lassen und damit dann die Float richtig erstellt werden, unabhänging ob nun Single oder Double...

https://en.wikipedia.org/wiki/Half-p...g-point_format

Ja da bin ich grad was am basteln.. Mal schauen

himitsu 6. Jul 2017 14:18

AW: Mal wieder HexToFloat
 
Das Umwandeln von HEX zu Float hat er doch schon?

Nur das weitere Umwandeln von Float zu "String mit Kommastellen" hing.

sko1 6. Jul 2017 14:31

AW: Mal wieder HexToFloat
 
Ich begreife immer noch nicht wo denn hier überhaupt Nachkommastellen herkommen sollen?

Wird 0000 übertragen ist das 0, wird FFFF übertragen ist das 65535

Ciao
Stefan

p80286 6. Jul 2017 14:40

AW: Mal wieder HexToFloat
 
Zitat:

Zitat von sko1 (Beitrag 1376155)
Wird 0000 übertragen ist das 0, wird FFFF übertragen ist das 65535

Nö, schau dir mal den Wikipedia-Link an.
es kommt ja darauf an wie die Bits interpretiert werden (müssen)

Gruß
K-H

gammatester 6. Jul 2017 15:03

AW: Mal wieder HexToFloat
 
Ich bleibe dabei, wir brauchen mehr Infos. Für $FFE4 würde - wie leicht aus der Wiki-Darstellung folgt - das Ergebnis ein NaN sein, da der Exponent binär 11111 ist.

himitsu 6. Jul 2017 15:31

AW: Mal wieder HexToFloat
 
Es sind 16 Bit als HEX gegeben.
Diese werden in Mantisse und Exponent zerlegt und in einen Float umgerechnet.
z.B. siehe WikiLink , aber welche Teile jetzt wieviele Bits haben, ist uns egal, den den Teil hat der TE bereits.

Das Problem was er hat, dass mit seine Float-To-Str-Funktion, welche er uns nicht verraten hat, eben im "falschen" Format raus kommt, wenn er den "Float" in einen menschenlesbaren String umwandeln will.
Und das läßt sich mit den richtigen passenden Umwandlungsfunktionen beheben, welche bereits genannt wurden.

Neutral General 6. Jul 2017 15:34

AW: Mal wieder HexToFloat
 
Falls es sich doch um ein 16-Bit Float handeln sollte habe ich mal was gebaut.
Ist sicherlich nicht das schönste auf der Welt, aber funktioniert soweit ich das getestet habe:

EDIT: Diese Version ist fehlerhaft! Ein paar Posts weiter unten ist die korrigierte Version!

Delphi-Quellcode:
function HexToFloat16(hex: String): Single;

  function FractionToFloat(frac: Cardinal): Single;
  var i: Integer;
  begin
    Result := 0;
    for i:=0 to 10 do
    begin
      if (frac shr (10-i)) <> 0 then
        Result := Result + Power(2, -(11-i));
    end;
  end;

var tmp: Word;
    resultBin: Cardinal;
    sign: Cardinal;
    exp: Cardinal;
    frac: Cardinal;
begin
  tmp := StrToInt('$' + hex);

  frac := (tmp and $3FF);
  exp := ((tmp shr 10) and $1F);;
  sign := (tmp shr 15);

  if (exp = $1F) then
  begin
    if (frac = 0) then
    begin
      if (Sign = 0) then
        Result := Infinity
      else
        Result := NegInfinity;
    end
    else
      Result := NaN;
  end
  else
  if (exp = 0) then
  begin
    if (frac = 0) then
    begin
      if (Sign = 0) then
        Result := 0
      else
        Result := -0;
    end
    else
    begin // Subnormals
      if (sign = 0) then
        Result := Power(2,-14) * FractionToFloat((frac shl 1) + 1)
      else
        Result := -Power(2,-14) * FractionToFloat((frac shl 1) + 1);
    end;
  end
  else
  begin
    resultBin := (frac shl 13) or ((exp - 15 + 127) shl 23) or (sign shl 31);
    Move(resultBin, Result, SizeOf(Cardinal));
  end;
end;

HolgerX 7. Jul 2017 07:52

AW: Mal wieder HexToFloat
 
Hmm..

@Neutral General

Irgendwie scheint die berechnung nicht zu passen..
Wenn der exp = 0 ist, dann kommen falsche Werte heraus.

Bei $0001 = 0 00000 0000000001

Kommt lt. Wiki 5,96046447753906E-8 heraus, es ist der minimum positive Subnormale.
Bei Dir leider 4,57763671875E-5

Wenn man einen Exponenten hat, dann passt deine Berechnung.
(Habe leider keine Möglichkeit (Fähigkeit) es selbst zu lösen ;) )



https://en.wikipedia.org/wiki/Half-p...g-point_format

Neutral General 7. Jul 2017 09:22

AW: Mal wieder HexToFloat
 
Korrigierte Version:

Delphi-Quellcode:
function HexToFloat16(hex: String): Single;

  function FractionToFloat(frac: Cardinal): Single;
  var i: Integer;
  begin
    Result := 0;
    for i:=0 to 10 do
    begin
      if ((frac shr i) and 1) <> 0 then
        Result := Result + Power(2, -(11-i));
    end;
  end;

var tmp: Word;
    resultBin: Cardinal;
    sign: Cardinal;
    exp: Cardinal;
    frac: Cardinal;
begin
  tmp := StrToInt('$' + hex);

  frac := (tmp and $3FF);
  exp := ((tmp shr 10) and $1F);;
  sign := (tmp shr 15);

  if (exp = $1F) then
  begin
    if (frac = 0) then
    begin
      if (Sign = 0) then
        Result := Infinity
      else
        Result := NegInfinity;
    end
    else
      Result := NaN;
  end
  else
  if (exp = 0) then
  begin
    if (frac = 0) then
    begin
      if (Sign = 0) then
        Result := 0
      else
        Result := -0;
    end
    else
    begin // Subnormals
      if (sign = 0) then
        Result := Power(2,-14) * FractionToFloat((frac shl 1))
      else
        Result := -Power(2,-14) * FractionToFloat((frac shl 1));
    end;
  end
  else
  begin
    resultBin := (frac shl 13) or ((exp - 15 + 127) shl 23) or (sign shl 31);
    Move(resultBin, Result, SizeOf(Cardinal));
  end;
end;

HolgerX 7. Jul 2017 09:58

AW: Mal wieder HexToFloat
 
Passt :thumb:

Jetzt nur noch die umgekehrte Version (von Single nach HEXStr) ... 8-) :wink:

echitna 7. Jul 2017 19:56

AW: Mal wieder HexToFloat
 
Zitat:

Zitat von gammatester (Beitrag 1376102)
Was soll das für ein Format sein? 4 Hex-Zeichen sind 16-Bit, das ist ein Format, das ungeöhnlich ist (single hat 32-Bit). Ohne Dokumentation des internen Aufbaus des 16-Bit-Wortes kann man wohl wenig sagen oder helfen.

Ja das ist leider ein ungewöhnliches Format, aber laut der Doku zu dem Gerät das ich anspreche ist das so verwendet. Ich habe es auch mit einem Port-Monitor geprüft, es werden wirklich nur vier Hex-Zeichen gesendet.

echitna 7. Jul 2017 19:57

AW: Mal wieder HexToFloat
 
Zitat:

Zitat von Neutral General (Beitrag 1376104)
Du kannst folgendes machen:
Delphi-Quellcode:
function HexToSingle(hex: String): Single;
var tmp: Cardinal;
begin
  tmp := StrToInt('$' + hex);
  Move(tmp, Result, SizeOf(Cardinal));
end;

var f: Single;
begin
  f := HexToSingle('FFE4');
  Caption := FloatToStr(f);
end;
Prinzipiell kannst du mit FloatToStrF oder Format die Zahl (wenn du sie als String ausgeben willst) auf X Nachkommastellen formatieren.
Hinweis: 9.17963e-41 ist NICHT 0,00000917. Die Darstellung "9.17963e-41" wird benutzt, weil die Zahl ausgeschrieben so aussehen würde:

0,0000000000000000000000000000000000000000917963

Sorry, war nur zu faul die vielen nullen zu tippen :)

echitna 7. Jul 2017 20:00

AW: Mal wieder HexToFloat
 
Vielen Dank für die vielen Tipps. Dann werde ich wohl mal eingiges ausprobieren wenn ich wieder dazu komme.

SirNox 15. Sep 2017 09:12

AW: Mal wieder HexToFloat
 
Hallo zusammen,

ich war gerade auch auf der Suche nach dieser Routine und hab vor diesen Forum folgendes gefunden:

https://gregstoll.dyndns.org/~gregstoll/floattohex/

Dort ist auch Code in anderer Sprache zu finden.

Ich hab die Hexwerte von Netzwerkprotokoll (BACnet) dort getestet und funktioniert:

0100 .... = Application Tag Number: Real (ANSI/IEE-754 floating point) (4)

present-value: 417.500000 (Real)
Hexwert: 43D0C000

Vielleicht bringt es was.

SirNox 15. Sep 2017 09:57

AW: Mal wieder HexToFloat
 
Ich habe nach dem Standard mal gesucht und folgendes gefunden:
http://www.delphipraxis.net/120032-i...l-wandeln.html

Bei mir hat es dann funktioniert:

Delphi-Quellcode:
var
  Data: array[0..3] of byte;
  X: Real;
begin
  Data[3] := aMsg.TheMessage[offset];
  Data[2] := aMsg.TheMessage[offset + 1];
  Data[1] := aMsg.TheMessage[offset + 2];
  Data[0] := aMsg.TheMessage[offset + 3];
  X := PSingle(@data)^;

Neutral General 15. Sep 2017 10:03

AW: Mal wieder HexToFloat
 
Edit: War Unsinn


Alle Zeitangaben in WEZ +1. Es ist jetzt 03:38 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz