Konwersja stringu w HEX na double

0

Mam plik tekstowy w formacie WKB, w którym są zapisane 8 bajtowe liczby typu double.
Ale nie są zapisane po ludzku, czyli jak np. 123.45, tylko jako obraz bitowy liczby w.g. IEEE 754 (znak, cecha, mantysa, jak w asemblerze) w hexach, np:

The X co-ordinate is 2.0
000000000140000000000000004010000000000000
The Y-co-ordinate is 4.0
000000000140000000000000004010000000000000

Czy sa w Delphi jakieś funkcje biblioteczne, które mogą pomóc w konwersji takiej liczby na double?

0

Tak na szybko - czy ten link rozwiązuje problem?
https://www.codeproject.com/Questions/99483/Convert-value-by-IEEE-754-protocol

0
cerrato napisał(a):

Tak na szybko - czy ten link rozwiązuje problem?
https://www.codeproject.com/Questions/99483/Convert-value-by-IEEE-754-protocol

Niestety, wygląda na to,że z Delphi nie ma dostępu do tego obiektu BitConverter

2

może to rozwiąże Twój problem//github.com/Tominator2/HEXtoIEEE-754

1

Oba powyższe rozwiązania bazują na liczbach pojedynczej precyzji.
Projekt z GitHub pewnie łatwiej będzie przerobić na double.

0

Wybaczcie pytanie delphistycznej blondynki.

To nie ma w D żadnych środków aby

  • wczytać hex string do tablicy bajtów
  • w/w tablicę zinterpretować jako double, rzutowanie, unia, czy jak tam nazywacie ...
1

Można ew. jeszcze spróbować z ASM:
Konwersja zmiennych IEEE 754

0

A co z konwersją na UInt64 i rzutowaniem na Double? Można też Move zrobić zamiast rzutowania.

Edit: jednak nie – te koordynaty w postaci hex nie pasują rozmiarowo do szesnastkowego UInt64. Pozostaje po prostu mała funkcja, która przekonwertuje fragmenty ciągu na inty i zbuduje liczbę rzeczywistą. Czyli to co podał @Paweł Dmitruk – bez zbędnych warunków i komentarzy zostanie 10 linijek kodu.

2

Jesteś pewny że to w ogóle dobre wartości? Po pierwsze mają po 21 bajtów co jest dość dziwnym rozmiarem dla typu liczbowego, po drugie podałeś dwa razy ten sam ciąg a mają one niby reprezentować różne wartości

edit: wygooglałem wklejony ciąg do googla

https://mariadb.com/kb/en/well-known-binary-wkb-format/

zgubiłeś pogrubienia:

  • The first byte indicates the byte order. 00 for big endian, or 01 for little endian.
  • The next 4 bytes indicate the geometry type. Values from 1 to 7 indicate whether the type is Point, LineString, Polygon, MultiPoint, MultiLineString, MultiPolygon, or GeometryCollection respectively.
  • The 8-byte floats represent the co-ordinates.

Czyli tylko "4000000000000000" reprezentuje "2.0" a "4010000000000000" reprezentuje "4.0"
Nie znam pascala (ostatni kod w nim pisałem jakieś 16 lat temu) ale pół minuty w googlach wystarczy żeby znaleźć:

https://ideone.com/GDQZfu

uses sysutils;

var
  xi : int64;
  yi : int64;
  x: double absolute xi;
  y: double absolute yi;
begin
	xi := StrToInt64('$4000000000000000');
	yi := StrToInt64('$4010000000000000');
	writeLn(x); // 2.0
	writeLn(y); // 4.0
end.
0

Dziękuję za pomoc.
Dla potomności pozostawiam elegancką funkcję:

function HexToDouble(txt:String):Double;
var
  xi : int64;
  x: double absolute xi;
begin
  xi := StrToInt64('$'+txt);
  result:=(x);
end;

Sposób użycia: HexToDouble('4000000000000000') da wynik 2.0

1

Dziwnie to wygląda. Masz przykład funkcji konwertujących we Free Pascalu – sprawdź czy w Delphi zadziała:

function DoubleToString(const AValue: Double): String; inline;
var
  Cast: Int64 absolute AValue;
begin
  Result := '$' + HexStr(Cast, SizeOf(Cast) * 2);
end;

function StringToDouble(const AValue: String): Double; inline;
var
  Cast: Int64 absolute Result;
  Dummy: Integer;
begin
  Val(AValue, Cast, Dummy);
end;

Test użycia:

var
  ValueAsString: String;
  ValueAsDouble: Double = Pi;
begin
  WriteLn('Double: "', ValueAsDouble:2:16, '"');

  ValueAsString := DoubleToString(ValueAsDouble);
  WriteLn('String: "', ValueAsString, '"');

  ValueAsDouble := 0;
  WriteLn('Double: "', ValueAsDouble:2:16, '"');

  ValueAsDouble := StringToDouble(ValueAsString);
  WriteLn('Double: "', ValueAsDouble:2:16, '"');
end.

I wyjście konsoli:

Double: "3.1415926535897931"  // wartość początkowa – tutaj Pi
String: "$400921FB54442D18"   // ciąg znaków po konwersji liczby zmiennoprzecinkowej
Double: "0.0000000000000000"  // wyzerowana wartość (żeby nie było że optymalizator wyciął numer)
Double: "3.1415926535897931"  // wartość po konwersji ciągu na liczbę zmiennoprzecinkową

Perfekcyjne odwzorowanie poprzedniego stanu.

0

Skoro jesteście tak chętni do pomocy, to może pomożecie mi w pracy detektywistycznej, rozkminienia tego formatu WKB
W pliku dostaję coś takiego:

0
0103000020840800000100000012000000B93313FCCDC61D418692C981207423415342B0EAFFC31D4140DD40A937742341728BF9091DC11D4138D7307B4F7423413D0D18A[....]

Z tego co sam doszedłem,
0 - zignorować
w stringu dwa pierwsze znaki 01 - to oznacza litle endian
0300 0020 8408 0000 0100 0000 1200 0000 - następne 32 znaki to jakiś nagłówek, bo we wszystkich plikach się powtarza podobny. W nim zgaduję, że 0100 - oznacza, że w pliku jest jakiś wielokąt (to się zgadza, powinna być granica działki), 1200 - to pewnie liczba wierzchołków ($12 czyli 18 wierzchołków)
dalej powinny być wspólrzędne, po kolei x y x y 18 razy
Zatem pierwsza liczba to B93313FCCDC61D41, ale jej konwersja ww. funkcją daje wynik ~-3,67 E-33, czyli bez sensu. Powinna być rozsądna liczba rzędu paruset tysięcy.
Macie jakiś pomysł co może być źle? Może ze względu na little endian trzeba poprzestawiać któreś bajty?

1

Tak przy 01 masz little endian czyli musisz odwrócić wszystkie bajty, przykładowo B93313FCCDC61D41 = 411DC6CDFC1333B9 - jako float by to było 487859.49616699998 ale nie wiem czy to prawidłowy fragment.
Nie ma tu co zgadywać - WKB to standard - sprawdź czy ten tool online potrafi to rozkodować:
https://rodic.fr/blog/online-conversion-between-geometric-formats/
korzysta z bilbioteki w js https://github.com/cschwarz/wkx
Może uda Ci się znaleźć coś do delphi, ale jeśli nie to zawsze możesz się wspomóc źródłami tej biblioteki żeby napisać własny kod

Tu masz jakąś funkcję do odwracania bajtów jako wstawka asm:

function BSwap64(I: QWORD): QWORD; { inline; }
asm
        MOV     EDX,[EAX]
        MOV     EAX,[EAX+4]
        BSWAP   EAX
        BSWAP   EDX
end;

Swoją drogą strasznie głupi standard który marnuje bajt na zapisanie informacji o endianness zamiast go po prostu... ustandaryzować

1 użytkowników online, w tym zalogowanych: 0, gości: 1