Discussion:
fread für long double
(zu alt für eine Antwort)
Markus Donath
2014-03-12 09:24:26 UTC
Permalink
Raw Message
Ich habe aus einem System Binärdateien, die Gleitkommawerte als 10-Byte
long double beinhalten. Die habe ich bisher so ausgelesen:

FILE* f = fopen("file.dat", "rb");
long double v;
fread(&v, sizeof(v) /* = 10 */, 1, f);
...

Ich möchte jetzt aber andere (C++)-Compiler verwenden. Bei diesen
scheint der Trend dazu zu gehen, entweder long double synonym zu double
zu verwenden (MSVC, clang, ...), d.h., long double hat nur 8 Byte oder
wie gcc 4.8.2 als 16-Byte-Gleitkommawert. Also steht mir kein
10-Byte-Typ zur Verfügung.

Gibt es eine Möglichkeit, die 10-Byte-Werte auszulösen, auch wenn der
Compiler keinen Typ dafür bereitstellt?

Markus
--
“Programming is like sex: It may give some concrete results, but that is
not why we do it.”
Bjarne Stroustrup with apologies to Richard Feynman
Juergen Ilse
2014-03-12 10:03:11 UTC
Permalink
Raw Message
Hallo,
Post by Markus Donath
Ich habe aus einem System Binärdateien, die Gleitkommawerte als 10-Byte
FILE* f = fopen("file.dat", "rb");
long double v;
fread(&v, sizeof(v) /* = 10 */, 1, f);
...
Ich möchte jetzt aber andere (C++)-Compiler verwenden. Bei diesen
scheint der Trend dazu zu gehen, entweder long double synonym zu double
zu verwenden (MSVC, clang, ...), d.h., long double hat nur 8 Byte oder
wie gcc 4.8.2 als 16-Byte-Gleitkommawert. Also steht mir kein
10-Byte-Typ zur Verfügung.
Binaerdarstellungen von Daten sind i.d.R. zwischen verschiedenen C-
Implementierungen nicht portabel. Lebe damit.
Post by Markus Donath
Gibt es eine Möglichkeit, die 10-Byte-Werte auszulösen, auch wenn der
Compiler keinen Typ dafür bereitstellt?
Wenn man die genaue Spezifikation des in der Datei abgelegten Formats kennt:
Sicher. Das ist aber nicht unbedingt der Fall, und wenn doch, muss man gffs.
damit rechenen, fuer jeden einzelnen Fall wieder separat eine Implementierung
bauen zu muessen, weil sich ggfs. die binarformate im Speicher unterscheiden
(z.B. aufgrund von unterschiedlicher Endianess oder unterschielichem Fliess-
kommerformat, oder ...).

Tschuess,
Juergen Ilse (***@usenet-verwaltung.de)
--
Ein Domainname ist nur ein Name, nicht mehr und nicht weniger.
Wer mehr hineininterpretiert, hat das Domain-Name-System nicht
verstanden.
Stefan Reuther
2014-03-12 17:38:34 UTC
Permalink
Raw Message
Post by Juergen Ilse
Post by Markus Donath
Ich möchte jetzt aber andere (C++)-Compiler verwenden. Bei diesen
scheint der Trend dazu zu gehen, entweder long double synonym zu double
zu verwenden (MSVC, clang, ...), d.h., long double hat nur 8 Byte oder
wie gcc 4.8.2 als 16-Byte-Gleitkommawert. Also steht mir kein
10-Byte-Typ zur Verfügung.
Binaerdarstellungen von Daten sind i.d.R. zwischen verschiedenen C-
Implementierungen nicht portabel. Lebe damit.
+1
Post by Juergen Ilse
Post by Markus Donath
Gibt es eine Möglichkeit, die 10-Byte-Werte auszulösen, auch wenn der
Compiler keinen Typ dafür bereitstellt?
Sicher. Das ist aber nicht unbedingt der Fall, und wenn doch, muss man gffs.
damit rechenen, fuer jeden einzelnen Fall wieder separat eine Implementierung
bauen zu muessen, weil sich ggfs. die binarformate im Speicher unterscheiden
(z.B. aufgrund von unterschiedlicher Endianess oder unterschielichem Fliess-
kommerformat, oder ...).
Sollte es sich bei dem erwähnten gcc 4.8.2 um einen Compiler für x86
handeln, so hat der Datentyp höchstwahrscheinlich weiterhin nur 10
Nutzbytes, aber aus Gründen des Speicher-Alignments noch 6 Padding-
Bytes. Dann kannst du deine Daten "retten", indem du erstmal weiterhin
mit 'fread(&v, 10, 1, fp)' arbeitest.

Zukunftssicher ist aber nur die Speicherung in einem Format, das du
komplett unter Kontrolle hast. C99 hat dafür das printf-Format "%a"
eingeführt, um binäre Floating-Point-Zahlen verlustfrei als Text
ausgeben zu können. Alternativ kannst du mittels 'frexpl' eine Zahl in
einen ganzzahligen Exponenten und eine Mantisse aufbrechen, aus der
Mantisse kannst du dann per Multiplikation mit Zweierpotenzen beliebig
viele Bits extrahieren.


Stefan
Bonita Montero
2014-03-12 15:32:39 UTC
Permalink
Raw Message
Heutige 64-Bit-Plattformen arbeiten nicht mehr mit der x87
-FPU, sondern mit SSE, so dass kein Arbeiten mehr mit 80
Bit FP-Zahlen möglich ist.
Und Du kannst eigentlich davon ausgehen, dass so ziemlich
alle relevanten Plattformen derzeit float 32-bittig und
double 64-bittig nach IEEE-754 implementieren (nicht aber
deren Endianess).
Die Unterstützung für 80-bittiges long double war nur mit
der x87-FPU möglich da nur die 80-bittige FP-Zahlen unter-
stützt. Aus Gründen der Kompatibilität zu anderen Plattfor-
men die mit maximal 64 Bit Genauigkeit rechnen können schal-
ten heutige Laufzeitumgebungen unter 32-Bit-Betriebssystemen
die precision-control-Bits des x87 FPU control-words auf 64
Bit Genauigkeit so dass die FPU-Ergebnisseso aussehen als
hättest Du nur eine FPU die 64-bittig arbeiten kann.
Und dass dir die Compiler keine 80-bittigen long doubles
mehr bieten tut sein Übriges.
Georg Bauhaus
2014-03-13 09:19:19 UTC
Permalink
Raw Message
Post by Bonita Montero
Heutige 64-Bit-Plattformen arbeiten nicht mehr mit der x87
-FPU, sondern mit SSE, so dass kein Arbeiten mehr mit 80
Bit FP-Zahlen möglich ist.
Es gibt zwar Prozessoren, die keine 80bit Fließkomma-Einheiten
haben. Es gibt auch viele relevanten Plattformen mit <= 32bit-Zahlen
und gar keiner hardware-Unterstützung für Fließkommazahlen.

Daraus zu schließen, dass
(a) der Bereich mit 32/64 bit abgedeckt wäre
(b) 80bit FPT nich möglich wäre
(c) die Wahl der Bitanzahl dem Programmierer entzogen wäre
ist nicht korrekt.

(a) 80bit werkelt in fast jedem Desktop; 128bit FPT ist im Kommen,
auch, aber nicht nur, in jüngeren SSE-Versionen. Zudem können
Implementierungen FPT-Büchereien nuzten; Speicherlängen scheinen
ggf. 96 und 128 bit zu sein.
(b) die 80bit-Zahlen lassen sich auf x87-Plattformen einschalten,
wenn sie das nicht ohnehin sind
(noch immer wichtig, wenn Genauigkeit der Zwischenergebnisse mehr
bedeutet, als Geschwindigkeit oder Parallelität der Rechnung).
(c) nicht jeder compiler für Intel-kompatible Plattformen
beschneidet die Unterstützung der floats auf 64 bits. Tatsächlich
will der Hersteller der MS compiler long double (>64bit) nur
auslaufen lassen. Bei den compilern von IBM, Intel, und GNU kann
man > 64bit für long double bekommen. Die Laufzeitumgebung wird
entsprechend eingestellt.

Vgl. die ensprechenden Dokumentationen der Hersteller.
Juergen Ilse
2014-03-13 09:54:08 UTC
Permalink
Raw Message
Hallo,
Post by Georg Bauhaus
Post by Bonita Montero
Heutige 64-Bit-Plattformen arbeiten nicht mehr mit der x87
-FPU, sondern mit SSE, so dass kein Arbeiten mehr mit 80
Bit FP-Zahlen möglich ist.
Es gibt zwar Prozessoren, die keine 80bit Fließkomma-Einheiten
haben. Es gibt auch viele relevanten Plattformen mit <= 32bit-Zahlen
und gar keiner hardware-Unterstützung für Fließkommazahlen.
Daraus zu schließen, dass
(a) der Bereich mit 32/64 bit abgedeckt wäre
Welche Fliesskommaformate es als native Datentypen in der jeweiligen
Implementierung vorhanden sind, ist vom Standard nicht festgelegt.
Daher kann man sich *nicht* darauf verlassen, dass ein bestimmter von
einem C-Compiler unterstuetzter Typ auch bei einem anderen C-Compiler
vorhanden ist.
Post by Georg Bauhaus
(b) 80bit FPT nich möglich wäre
Natuerlich ist es moeglich, aber ggfs. muss man dann alle Routinen fuer
den Umgang mit einem solchen Typ selbst implementieren und muss ggfs. auf
die Nutzung der arithmetischen Operatoren fuer diesen Typ verzichten (dann,
wenn ein solcher Datentyp nicht "native" von der Implementierung unter-
stuetzt wird).
Post by Georg Bauhaus
(c) die Wahl der Bitanzahl dem Programmierer entzogen wäre
ist nicht korrekt.
Auf die Formate der "native unterstuetzten Fliesskommaformate" hat der
Programmierer in C tatsaechlich keinen Einfluss.
Post by Georg Bauhaus
(a) 80bit werkelt in fast jedem Desktop; 128bit FPT ist im Kommen,
auch, aber nicht nur, in jüngeren SSE-Versionen. Zudem können
Implementierungen FPT-Büchereien nuzten; Speicherlängen scheinen
ggf. 96 und 128 bit zu sein.
Es ging in dieser Diskussion IMHO nicht darum, was eine Implementierung
unterstuetzen koennte, sondern wie man damit umgeht, wenn eine Implemen-
tierung ein bestimmtes Fliesskommaformat nicht native unterstuetzt.
Post by Georg Bauhaus
(b) die 80bit-Zahlen lassen sich auf x87-Plattformen einschalten,
wenn sie das nicht ohnehin sind
(noch immer wichtig, wenn Genauigkeit der Zwischenergebnisse mehr
bedeutet, als Geschwindigkeit oder Parallelität der Rechnung).
Der Sprachstandard der Sprache C kennt weder "x87-Plattform" noch eine
Moeglichkeit, ein bestimmtes Fliesskommaformat einzuschalten. Manche
Implementierungen moegen (ueber den Standard hinausgehende) Loesungen
fuer so etwas bieten, aber diese Erweiterungen sind dann in dieser Gruppe
OffTopic.
Post by Georg Bauhaus
(c) nicht jeder compiler für Intel-kompatible Plattformen
beschneidet die Unterstützung der floats auf 64 bits. Tatsächlich
will der Hersteller der MS compiler long double (>64bit) nur
auslaufen lassen. Bei den compilern von IBM, Intel, und GNU kann
man > 64bit für long double bekommen. Die Laufzeitumgebung wird
entsprechend eingestellt.
Diskussionen ueber bestimmte implementationsspezifische Eigenschaften
bestimmter Compiler sind in dieser Gruppe i.a. eher OffTopic.

Tschuess,
Juergen Ilse (***@usenet-verwaltung.de)
--
Ein Domainname ist nur ein Name, nicht mehr und nicht weniger.
Wer mehr hineininterpretiert, hat das Domain-Name-System nicht
verstanden.
Thomas Richter
2014-03-13 11:04:32 UTC
Permalink
Raw Message
Post by Juergen Ilse
Hallo,
Post by Georg Bauhaus
Es gibt zwar Prozessoren, die keine 80bit Fließkomma-Einheiten
haben. Es gibt auch viele relevanten Plattformen mit <= 32bit-Zahlen
und gar keiner hardware-Unterstützung für Fließkommazahlen.
Daraus zu schließen, dass
(a) der Bereich mit 32/64 bit abgedeckt wäre
Welche Fliesskommaformate es als native Datentypen in der jeweiligen
Implementierung vorhanden sind, ist vom Standard nicht festgelegt.
Daher kann man sich *nicht* darauf verlassen, dass ein bestimmter von
einem C-Compiler unterstuetzter Typ auch bei einem anderen C-Compiler
vorhanden ist.
Post by Georg Bauhaus
(b) 80bit FPT nich möglich wäre
Natuerlich ist es moeglich, aber ggfs. muss man dann alle Routinen fuer
den Umgang mit einem solchen Typ selbst implementieren und muss ggfs. auf
die Nutzung der arithmetischen Operatoren fuer diesen Typ verzichten (dann,
wenn ein solcher Datentyp nicht "native" von der Implementierung unter-
stuetzt wird).
Oder alternativ eine Umwandlung der externen IEEE-Zahlen in die interne
Fließkommadarstellung vornehmen. Das ist mit Mitteln des Standards, etwa
über ldexp(), problemlos möglich. Eventuell kann dabei natürlich
Genauigkeit verloren gehen, oder bestimmte IEEE-spezifische Werte wie
NANs oder INFs können nicht dargestellt werden.

Folgendes würde etwa für 32-Bit funktionieren, mit geeigneten Werten für
sshift (sign-Bit-Position), eshift (exponent-shift Position) und emask:

/// InternalIEEETon
// Portably convert an arbitrarely coded floating point number
// following the specs of part-2 into a FLOAT. This is the slow
// version if we cannot ensure that the FLOAT data type is
// IEEE single precision.
// Parameters are: the shift to shift the internal represented
// exponent to zero, the mask to extract it then.
float InternalIEEEToFloat(long in,int sshift,int eshift,int emask)
{
int sign = in >> sshift;
long exponent = (in >> eshift) & emask; // extract the exponent.
long mantissa = in & ((1UL << eshift) - 1);

if (exponent != 0) { // normalized number.
if (exponent == emask) { // INF or NAN?
if (mantissa == 0) {
if (sign) {
return -HUGE_VAL;
} else return HUGE_VAL;
} else { // a NAN.
#ifdef NAN
return NAN;
#else
return 0.0f; // unclear, host system may not have NANs.
#endif
}
} else { // not INF or NAN. Normalized.
float out = ldexp((float)(mantissa | (1UL << eshift)),exponent -
(emask >> 1) - eshift);
if (sign) {
return -out;
} else {
return out;
}
}
} else { // denormalized number.
float out = ldexp((float)(mantissa),1 - (emask >> 1) - eshift);
if (sign) {
return -out;
} else {
return out;
}
}
}
///
Gerald Breuer
2014-03-13 10:08:02 UTC
Permalink
Raw Message
(a) 128bit FPT ist im Kommen, auch, aber nicht nur,
in jüngeren SSE-Versionen.
SSE unterstützt maximal 64-bittige FP-Zahlen.
Was 128-bittig ist ist das Bundlle aus zwei oder vier FP-Zahlen.
Gerald Breuer
2014-03-13 16:26:49 UTC
Permalink
Raw Message
Post by Bonita Montero
Die Unterstützung für 80-bittiges long double war nur mit
der x87-FPU möglich ...
Und mit den FPUs der Motorola 68K-Serie.

Thomas Rachel
2014-03-13 10:22:11 UTC
Permalink
Raw Message
Post by Markus Donath
Gibt es eine Möglichkeit, die 10-Byte-Werte auszulösen, auch wenn der
Compiler keinen Typ dafür bereitstellt?
Eine Möglichkeit gibt es immer.

Zur Not müßtest Du, sofern Du davon ausgehen kannst, daß die Dateien
alle mit demselben Format geschrieben wurden, die Felder manuell parsen.

https://de.wikipedia.org/wiki/IEEE_754 verrät Dir, wie die Einteilung
der Felder ist.

Allerdings kann das beim x87-spezifischen 80-Bit-Format nochmal
abweichen; ich meine mich zu erinnern, daß es hier kein "implizites" Bit
gibt.


Thomas
Thomas Rachel
2014-03-13 10:32:59 UTC
Permalink
Raw Message
Post by Thomas Rachel
https://de.wikipedia.org/wiki/IEEE_754 verrät Dir, wie die Einteilung
der Felder ist.
Allerdings kann das beim x87-spezifischen 80-Bit-Format nochmal
abweichen; ich meine mich zu erinnern, daß es hier kein "implizites" Bit
gibt.
https://en.wikipedia.org/wiki/Extended_precision
Damit sollte das De- und Umkodieren kein Problem mehr darstellen.

Thomas
Loading...