Datentyp extended

Für alles, was in den übrigen Lazarusthemen keinen Platz, aber mit Lazarus zutun hat.
Benutzeravatar
Lincoln Six Echo
Beiträge: 138
Registriert: Di 26. Aug 2014, 16:42
OS, Lazarus, FPC: Win10, Debian
CPU-Target: I7/I9/Q9650/u.a.
Wohnort: Hamburg

Datentyp extended

Beitrag von Lincoln Six Echo »

Aus einer Appledatei müssen Daten ausgelesen werden, die Datei ist im Motorola 68000 Format also Big Endian.

Alles klappt fehlerfrei mit SwapEndian solange es sich um integer, word, longword usw dreht.

Beim Datentyp Extended aber kommen nur falsche Werte heraus obwohl ich die zehn byte auch "geswappt" habe.

Hat jemand dazu eine Idee ?

Benutzeravatar
photor
Beiträge: 445
Registriert: Mo 24. Jan 2011, 21:38
OS, Lazarus, FPC: Arch Linux: L 2.2.6 FPC 3.2.2 (Gtk2)
CPU-Target: 64Bit

Re: Datentyp extended

Beitrag von photor »

Lincoln Six Echo hat geschrieben:
Mi 11. Okt 2023, 20:01
Aus einer Appledatei müssen Daten ausgelesen werden, die Datei ist im Motorola 68000 Format also Big Endian.

Alles klappt fehlerfrei mit SwapEndian solange es sich um integer, word, longword usw dreht.

Beim Datentyp Extended aber kommen nur falsche Werte heraus obwohl ich die zehn byte auch "geswappt" habe.

Hat jemand dazu eine Idee ?
Hallo,

ist schon ewig her, dass ich mit Motorola 68000 zu tun hatte (ATARI ST) - daher mit Vorsicht genießen.

Aber bist du sicher, dass die Float-Daten im Extended-Format (10 Byte) gespeicher werden und nicht als "normale" Double (8-Byte)? Ich weiß nicht, ob die 68000 damit ordentlich umgehen konnten. Und das Extended-Format war - soweit ich weiß - ein rein internes Format, das nicht zum Datenaustausch gedacht war.

Vielleicht versuchst du mal mit dem "Double", ob das sinnvollere Zahlen ergibt.

Ciao,
Photor

Benutzeravatar
Lincoln Six Echo
Beiträge: 138
Registriert: Di 26. Aug 2014, 16:42
OS, Lazarus, FPC: Win10, Debian
CPU-Target: I7/I9/Q9650/u.a.
Wohnort: Hamburg

Re: Datentyp extended

Beitrag von Lincoln Six Echo »

Es sind tatsächlich 10 byte denn die Werte die ich danach auslese sind wieder korrekt.

Die Dateien stammen übrigens aus dem Jahr 1988.

Hier steht unter "extended" 80 Bit. Das sind ja 10 byte.

https://wiki.freepascal.org/IEEE_754_formats

Joh
Lazarusforum e. V.
Beiträge: 191
Registriert: Sa 26. Mai 2012, 17:31
OS, Lazarus, FPC: Win 10 (L 2.2.6 x64 FPC 3.2.2)
CPU-Target: 64Bit

Re: Datentyp extended

Beitrag von Joh »

Da du beide Daten hast:
- Rohdaten 10bit und
- Integerwerte(soll),
sollten doch ein paar Beispiele reichen, um das tatsächlich gespeicherte Format herauszubekommen.

z.B.: 0000101011 soll 137 dezimal ergeben
just my two Beer

Socke
Lazarusforum e. V.
Beiträge: 3158
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: Datentyp extended

Beitrag von Socke »

Lincoln Six Echo hat geschrieben:
Mi 11. Okt 2023, 20:41
Hier steht unter "extended" 80 Bit. Das sind ja 10 byte.

https://wiki.freepascal.org/IEEE_754_formats
Dort steht auch, dass extended nicht auf allen Plattformen unterstützt wird. Konkret ist das meines Wissens nach nur Intel x86. Wenn du auf einer anderen CPU-Archtitektur unterwegs bist - dazu zählt auch x86_64 - hast du kein 10-Bit Extended.

Wie Joh schon schreibt stellst du am besten ein paar Beispieldaten und deinen Code zum Umformung bereit. Im besten Fall hast du auch die erwarteten Zahlen im Dezimalformat.
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

Benutzeravatar
Lincoln Six Echo
Beiträge: 138
Registriert: Di 26. Aug 2014, 16:42
OS, Lazarus, FPC: Win10, Debian
CPU-Target: I7/I9/Q9650/u.a.
Wohnort: Hamburg

Re: Datentyp extended

Beitrag von Lincoln Six Echo »

Das der Datentyp extended keine 10 byte hat ist klar, vielleicht habe ich es falsch ausgedrückt.

Der Datentyp "Extended" mit 10 byte bezieht sich auf die Notation in der Datei, d.h. in der Datei wird der Wert als 10 byte (80 bit) Float gespeichert, das ganze dann im Motorola 68000 Format.

Code: Alles auswählen

TData = record
  SFX : word;
  CHS : longword;
  Ext : array[0..9] of byte;
  ANC : word;
  BSX : word;
end;

SFX := SwapEndian(SFX);
CHS := SwapEndian(CHS);
ANC := SwapEndian(ANC);
BSX := SwapEndian(BSX);
SFX, CHS, ANC und BSX liefern so korrekte Werte. Nur mit Ext kann ich machen was ich will, es kommen immer nur falsch Werte.

Apple sagt: "extended = 80 bit IEEEE Standard 754 floating point number"

https://de.wikipedia.org/wiki/IEEE_754

Auch beim Schreiben einer solchen Datei ist das Problem wie den Wert in die 10 byte zu bekommen das auch andere Programme die Daten korrekt lesen.

Warf
Beiträge: 1913
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Datentyp extended

Beitrag von Warf »

Das Problem mit extended ist, es gibt nicht das Extended Format. Extended bedeutet einfach nur mehr als Double aber weniger als Quadruple (128 bit float). Der Grund ist relativ simpel, als die Spec geschrieben wurde war es technisch einfach nicht wirklich möglich effiziente FPUs für Quadruple Precision Floats zu bauen, die 112 bit Mantissen effizient berechnen können. Daher war dann der Kompromiss, es wir ein spezialtyp "Extended" engeführt, der mindestens Double ist, bei dem die Prozessorhersteller dann das beste bereitstellen können was sie Bauen können.

Intel hat mantissen bis zu 64 bit hinbekommen, also ist auf intel x86 Extended 64 bit Mantisse + 15 bit Exponent + 1 Sign bit = 80 bit.
Das ist aber nur intel x86, die IEE 754 Spec sieht explizit keine Interoperabilität von Extended vor, da diese eigentlich nur eine Übergangslösung sein sollten. Eigentlich war der Plan das Programmierer keine Annahmen über das Speicherlayout treffen sollten, damit dann über die Zeit die Prozessorhersteller immer größere Extended bauen können bis sie irgendwann Quadruples erreicht haben, welche dann wieder Wohlspezifiziert sind.

Jetzt ist aber natürlich das Problem, das wenn Programmierer eins können, dann denken das sie es besser wissen als die Spec, und wenn in der Spec steht das man keine Annahmen treffen soll, sie denken "Ich weis doch auf was für einer CPU das läuft" und dann trozdem Annahmen treffen. Deshalb ist das konzept "Extended" gescheitert, und wird Grundsätzlich auch nicht weiter verfolgt. Das führt dann dazu das Extended ein Relikt von ein paar spezifischen Architekturen ist, die obwohl es alle "Extended" nennen, miteinander nicht kompatibel sein müssen. Um ein Beispiel von WIkipedia zu nennen:
The Motorola 6888x math coprocessors and the Motorola 68040 and 68060 processors support this same 64-bit significand extended precision type (similar to the Intel format although padded to a 96-bit format with 16 unused bits inserted between the exponent and significand fields[9]). The follow-on Coldfire processors do not support this 96-bit extended precision format.
Die haben also einen 80 bit Extended, der aber in 96 bit gespeichert wird, denn wie bereits gesagt, jeder Prozessorhersteller darf hier sein eigenes Bier brauen.

Langer Rede kurzer Sinn, das bedeutet vor allem das es keinen einfachen weg gibt 1. "big endian extended" überhaupt einzulesen, und 2. in ein system natives format zu konvertieren.

Was du also machen musst ist dir das entsprechende Manual von dem CPU typen den du da vor dir hast rauszusuchen, und dort genau nachlesen was die für ein Format verwendet haben für extended. Und schließlich das dann in das Native format deiner CPU umrechnen.

Ein beispiel wie man Intel like 80 bit Extended im Big Endian format Arithmetisch umrechnen kann (nicht getestet):

Code: Alles auswählen

TExtRecordBE=record
  // 1. bit sign bit 15 rest bits exponent
  exp: Word;
  // 64 bit mantisse
  Mantissa:QWord;
end;

function toExtended(extData:TExtRecordBE):Extended;
var
  exp: Integer;
begin
  // Exponent nehmen, endianess swappen und sign bit entfernen
  exp:=ntohs(extData.exp) And $7FFF;
  // Exponent auf negativ range normalisieren
  exp:=exp-$3FFF;
  // Ergebnis ist mantisse * 2^-63 * 2^exp = m*2^(exp-63)
  Result:=ntohll(extData.Mantissa)*pow(2, exp-63);
  // Sign drauf multiplizieren
  if (ntohs(extData.exp) And $8000) = 1 then
    Result *= -1;
end;

Benutzeravatar
Lincoln Six Echo
Beiträge: 138
Registriert: Di 26. Aug 2014, 16:42
OS, Lazarus, FPC: Win10, Debian
CPU-Target: I7/I9/Q9650/u.a.
Wohnort: Hamburg

Re: Datentyp extended

Beitrag von Lincoln Six Echo »

Wie und in welchem Format genau diese 10 byte aussehen ist lediglich mit dem Hinweis auf IEEE 754 usw. abgetan.

Das Code-Beispiel funktioniert nicht.

ntohs usw gibt es meines Wissens in Pascal nicht.

Die 10 byte aus der Datei gelesen sehen so aus: (decimal)

64 14 172 68 0 0 0 0 0 0

Ich weiß das es 44100,0 ergeben muß weil alle Programme mir das so anzeigen.

Benutzeravatar
Lincoln Six Echo
Beiträge: 138
Registriert: Di 26. Aug 2014, 16:42
OS, Lazarus, FPC: Win10, Debian
CPU-Target: I7/I9/Q9650/u.a.
Wohnort: Hamburg

Re: Datentyp extended

Beitrag von Lincoln Six Echo »

Das soll die Lösung sein, ich kann leider zu wenig C um das umzusetzen.
c-code.jpg
c-code.jpg (31.44 KiB) 3278 mal betrachtet

wp_xyz
Beiträge: 4893
Registriert: Fr 8. Apr 2011, 09:01

Re: Datentyp extended

Beitrag von wp_xyz »

Das folgende Programm spuckt dafür etwas sinnvolles aus:

Code: Alles auswählen

program project1;
uses
  SysUtils;

procedure SwapBytes(var ex: Extended);
var
  arr1: array[0..9] of byte;
  arr2: array[0..9] of byte;
  i: Integer;
begin
  Move(ex, arr1, SizeOf(arr1));
  for i := 0 to 9 do
    arr2[9-i] := arr1[i];
  Move(arr2, ex, sizeOf(arr2));
end;

var
  ex: Extended;
  ex10: array[0..9] of byte absolute ex;
  i: Integer;
begin
  ex := 44100.0;

  WriteLn('Little Endian');
  WriteLn(Format('%.15g', [ex]));
  for i := 0 to 9 do
    Write(Format('$%.2x ', [ex10[i]]));
  WriteLn;
  for i := 0 to 9 do
    Write(Format('%d ', [ex10[i]]));
  WriteLn;

  WriteLn();
  WriteLn('Big Endian');
  SwapBytes(ex);
  WriteLn(Format('%.15g', [ex]));
  for i := 0 to 9 do
    Write(Format('$%.2x ', [ex10[i]]));
  WriteLn;
  for i := 0 to 9 do
    Write(Format('%d ', [ex10[i]]));

  WriteLn;
  ReadLn;
end.
      
(Muss als 32-bit Anwendung unter Windows laufen, denn dort gibt es den Typ Extended).

Demnach ist die oben genannte Bytefolge (von links nach rechts gelesen) tatsächlich Extended und im Little-Endian Format.

wp_xyz
Beiträge: 4893
Registriert: Fr 8. Apr 2011, 09:01

Re: Datentyp extended

Beitrag von wp_xyz »

Lincoln Six Echo hat geschrieben:
Do 12. Okt 2023, 12:03

Code: Alles auswählen

TData = record
  SFX : word;
  CHS : longword;
  Ext : array[0..9] of byte;
  ANC : word;
  BSX : word;
end;
SFX, CHS, ANC und BSX liefern so korrekte Werte. Nur mit Ext kann ich machen was ich will, es kommen immer nur falsch Werte.
Also meiner Meinung nach werden die Bytes eines Byte-Arrays auch im Motorola-Format so geschrieben, dass zuerst Index 0 kommt, dann Index 1, usw. Bildest du das Array[0..9] of Byte dann auf einem 10-byte Extended-Wert ab, dann ist dieser im Little-Endian Format (denn der Byte-Stream beginnt mit der niedrigsten Wertigkeit, genauso wie bei einem Little-Endian-Stream), auch wenn der Rest der Datei (SFX, CHS, ANC, BSX) Big-Endian ist.
Zuletzt geändert von wp_xyz am Do 12. Okt 2023, 15:46, insgesamt 1-mal geändert.

Warf
Beiträge: 1913
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Datentyp extended

Beitrag von Warf »

Lincoln Six Echo hat geschrieben:
Do 12. Okt 2023, 15:09
ntohs usw gibt es meines Wissens in Pascal nicht.
In der sockets unit müssten sie definiert sein. Aber ich glaube nur htonl und htons existiert, htonll fehlt.
Lincoln Six Echo hat geschrieben:
Do 12. Okt 2023, 15:09
Die 10 byte aus der Datei gelesen sehen so aus: (decimal)

64 14 172 68 0 0 0 0 0 0

Ich weiß das es 44100,0 ergeben muß weil alle Programme mir das so anzeigen.
Hab packed beim record vergessen, dann gehts:

Code: Alles auswählen

function ntohll(ll:QWord):QWord;
begin
  Result:=0;
  PByte(@Result)[0]:=PByte(@ll)[7];
  PByte(@Result)[1]:=PByte(@ll)[6];
  PByte(@Result)[2]:=PByte(@ll)[5];
  PByte(@Result)[3]:=PByte(@ll)[4];
  PByte(@Result)[4]:=PByte(@ll)[3];
  PByte(@Result)[5]:=PByte(@ll)[2];
  PByte(@Result)[6]:=PByte(@ll)[1];
  PByte(@Result)[7]:=PByte(@ll)[0];
end;

type
  TExtRecordBE=packed record
  // 1. bit sign bit 15 rest bits exponent
  exp: Word;
  // 64 bit mantisse
  Mantissa:QWord;
end;

function toExtended(extData:TExtRecordBE):Extended;
var
  exp: Integer;
begin
  // Exponent nehmen, endianess swappen und sign bit entfernen
  exp:=ntohs(extData.exp) And $7FFF;
  // Exponent auf negativ range normalisieren
  exp:=exp-$3FFF;
  // Ergebnis ist mantisse * 2^-63 * 2^exp = m*2^(exp-63)
  Result:=Extended(ntohll(extData.Mantissa))*Power(2, exp-63);
  // Sign drauf multiplizieren
  if (ntohs(extData.exp) And $8000) = 1 then
    Result *= -1;
end;

Benutzeravatar
Lincoln Six Echo
Beiträge: 138
Registriert: Di 26. Aug 2014, 16:42
OS, Lazarus, FPC: Win10, Debian
CPU-Target: I7/I9/Q9650/u.a.
Wohnort: Hamburg

Re: Datentyp extended

Beitrag von Lincoln Six Echo »

Es ergibt aber etwas anderes als die in der Datei enthaltenen 64 14 172 68 0 0 0 0 0 0:

Code: Alles auswählen

Little Endian
44100
$00 
$00 
$00 
$00 
$80 
$88 
$E5 
$40 
$00 
$00 

0 
0 
0 
0 
128 
136 
229 
64 
0 
0 


Big Endian
6,98240497697302E-310
$00 
$00 
$40 
$E5 
$88 
$80 
$00 
$00 
$00 
$00 

0 
0 
64 
229 
136 
128 
0 
0 
0 
0 

Mathias
Beiträge: 6209
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Datentyp extended

Beitrag von Mathias »

Da habe ich jetzt nicht schlecht gestaunt, dies wird tatsächlich kompiliert.
Vor allem links vom := sieht echt merkwürdig aus.

Code: Alles auswählen

function ntohll(ll:QWord):QWord;
begin
  Result:=0;
  PByte(@Result)[0]:=PByte(@ll)[7];
  PByte(@Result)[1]:=PByte(@ll)[6];
  PByte(@Result)[2]:=PByte(@ll)[5];
  PByte(@Result)[3]:=PByte(@ll)[4];
  PByte(@Result)[4]:=PByte(@ll)[3];
  PByte(@Result)[5]:=PByte(@ll)[2];
  PByte(@Result)[6]:=PByte(@ll)[1];
  PByte(@Result)[7]:=PByte(@ll)[0];
end;
Ich hätte sowas mit mit and, or, not, shl, etc. gelöst.
Das sowas geht hätte ich nie gedacht.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Warf
Beiträge: 1913
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Datentyp extended

Beitrag von Warf »

Lincoln Six Echo hat geschrieben:
Do 12. Okt 2023, 15:57
Es ergibt aber etwas anderes als die in der Datei enthaltenen 64 14 172 68 0 0 0 0 0 0:
Was meinst du damit? Hier ist mein testprogramm:

Code: Alles auswählen

program Project1;

{$mode objfpc}{$H+}

uses
  sockets,math;

function ntohll(ll:QWord):QWord;
begin
  {$IFDEF ENDIAN_LITTLE}
  Result:=0;
  PByte(@Result)[0]:=PByte(@ll)[7];
  PByte(@Result)[1]:=PByte(@ll)[6];
  PByte(@Result)[2]:=PByte(@ll)[5];
  PByte(@Result)[3]:=PByte(@ll)[4];
  PByte(@Result)[4]:=PByte(@ll)[3];
  PByte(@Result)[5]:=PByte(@ll)[2];
  PByte(@Result)[6]:=PByte(@ll)[1];
  PByte(@Result)[7]:=PByte(@ll)[0];
  {$Else}
  Result:=ll;
  {$Endif}
end;

type
  PExtRecordBE=^TExtRecordBE;
  TExtRecordBE=packed record
  // 1. bit sign bit 15 rest bits exponent
  exp: Word;
  // 64 bit mantisse
  Mantissa:QWord;
end;

function toExtended(extData:TExtRecordBE):Extended;
var
  exp: Integer;
begin
  // Exponent nehmen, endianess swappen und sign bit entfernen
  exp:=ntohs(extData.exp) And $7FFF;
  // Exponent auf negativ range normalisieren
  exp:=exp-$3FFF;
  // Ergebnis ist mantisse * 2^-63 * 2^exp = m*2^(exp-63)
  Result:=Extended(ntohll(extData.Mantissa))*Power(2, exp-63);
  // Sign drauf multiplizieren
  if (ntohs(extData.exp) And $8000) = 1 then
    Result *= -1;
end;

var
  ExtData: Array[0..9] of Byte = (64, 14, 172, 68, 0, 0, 0, 0, 0, 0);
begin
  WriteLn(toExtended(PExtRecordBE(@ExtData)^)); //  4.4100000000000000E+004
  ReadLn;
end.
Ich hab einfach deine bytes 1-1 in einen Array abgetippt und das ganze dann in die Funktion geworfen.

Ist getestet auf x86_64 Windows (little endian) wo Extended = Double ist (also findet hier tatsächlich auch ein Informationsverlust von 16 bit statt, da x86_64 windows kein 80 bit Extended mehr unterstützt). Sollte aber durch die nutzung von ntohs und ntohll aber theoretisch auf jedem System laufen mit jeder art von Extended typen

Antworten