Long Double (GCC-spezifisch) und __float128
Ich bin auf der Suche nach detaillierten Informationen über long double
und __float128
im GCC/x86 (mehr aus Neugier als wegen eines aktuellen Problems).
Paar Leute werden wahrscheinlich jemals brauchen werden, diese (ich habe gerade zum ersten mal wirklich benötigt eine double
), aber ich denke, es ist immer noch interessant (und interessant) zu wissen, was Sie in der toolbox und dem, was es ist.
In diesem Licht, bitte entschuldigen Sie meine etwas offene Fragen:
- Könnte mir jemand erklären, die Umsetzung, Begründung und geplante Verwendung von diesen Arten, auch im Vergleich miteinander? Zum Beispiel, werden Sie "Verlegenheit Implementierungen", denn der standard ermöglicht es, den Typ, und jemand könnte sich beschweren, wenn Sie gerade erst die gleiche Präzision wie
double
, oder Sie sind gedacht als erste-Klasse-Typen? - Alternativ, hat jemand eine gute, brauchbare web-Verweis zu teilen? Eine Google-Suche auf
"long double" site:gcc.gnu.org/onlinedocs
mir nicht viel, dass ist wirklich nützlich. - Unter der Annahme, dass das gemeinsame mantra "wenn Sie glauben, dass Sie brauchen Doppel -, werden Sie wahrscheinlich nicht verstehen, floating point" gilt nicht, d.h. Sie wirklich brauchen mehr Präzision als nur
float
, und man ist nicht egal, ob 8 oder 16 bytes des Speichers sind verbrannt... ist es vernünftig zu erwarten, dass man genauso gut springen zulong double
oder__float128
stattdouble
ohne eine signifikante Auswirkung auf die Leistung? - Die "extended-precision" - Funktion von Intel-CPUs seit jeher Quelle der bösen überraschungen, wenn die Werte verschoben wurden zwischen Speicher und Register. Wenn tatsächlich 96 bits gespeichert werden, die
long double
geben sollte, beseitigen Sie dieses Problem. Auf der anderen Seite verstehe ich, dasslong double
Typ ist gegenseitig mit-mfpmath=sse
werden, da es keine solche Sache als "extended precision" in der SSE.__float128
, auf der anderen Seite, sollten die arbeiten nur einwandfrei mit SSE Mathe (obwohl in der Abwesenheit von quad Präzisions-Anweisungen, die sicherlich nicht auf eine 1:1 Anleitung base). Gehe ich Recht in der diese Annahmen?
(3. und 4. kann man wohl herausgefunden, mit der Arbeit, die aufgewendet profiling-und Abbau, aber vielleicht hat jemand anderes den gleichen Gedanken hatte vorher schon gemacht hat, die Arbeit.)
Hintergrund (das ist die TL;DR-Teil):
Ursprünglich habe ich stolperte über long double
da ich auf der Suche bis DBL_MAX
im <float.h>
, und incidentially LDBL_MAX
ist auf der nächsten Zeile. "Oh, schau, GCC hat tatsächlich 128 bit verdoppelt, nicht, dass ich Sie brauche, aber... cool" war auch mein Erster Gedanke. Überraschung, überraschung: sizeof(long double)
gibt 12... warte, meinst du 16?
C und C++ standards, die wenig überraschend nicht geben eine sehr konkrete definition von Art. C99 (6.2.5 10) sagt, dass die zahlen von double
sind eine Teilmenge der long double
in der Erwägung, dass C++03-Staaten (3.9.1 8), die long double
hat mindestens ebenso viel Präzision wie double
(das ist das gleiche, nur anders formuliert). Grundsätzlich werden die standards verlassen alles, um die Umsetzung in der gleichen Weise wie mit long
, int
, und short
.
Wikipedia sagt, dass GCC verwendet "80-bit-extended-precision auf x86-Prozessoren unabhängig von der physischen Speicherung verwendet".
Die GCC-Dokumentation angegeben, sind alle auf der gleichen Seite, dass die Größe des Typs ist 96 bits, da der i386-ABI, aber nicht mehr als 80 bit Genauigkeit werden aktiviert, wenn eine beliebige option (huh? was?), auch Pentium und neuere Prozessoren wollen Sie ausgerichtet, wie 128-bit-zahlen. Dies ist die Standardeinstellung unter 64 bit und kann manuell aktiviert werden unter 32 bits, was 32 bits von null Polsterung.
Zeit, um einen test:
#include <stdio.h>
#include <cfloat>
int main()
{
#ifdef USE_FLOAT128
typedef __float128 long_double_t;
#else
typedef long double long_double_t;
#endif
long_double_t ld;
int* i = (int*) &ld;
i[0] = i[1] = i[2] = i[3] = 0xdeadbeef;
for(ld = 0.0000000000000001; ld < LDBL_MAX; ld *= 1.0000001)
printf("%08x-%08x-%08x-%08x\r", i[0], i[1], i[2], i[3]);
return 0;
}
Den Ausgang, wenn mit long double
, sieht etwas aus wie diese, mit markierten Ziffern konstant, und alle anderen, schließlich ändern sich die zahlen immer größer werden:
5636666b-c03ef3e0-00223fd8-deadbeef
^^ ^^^^^^^^
Dies deutet darauf hin, dass es nicht eine 80 bit Zahl. Ein 80-bit-Zahl hat 18 hex-Ziffern. Ich sehe, 22 hex-Ziffern ändern, die sieht viel eher wie ein 96-bit-Zahl (24 hex Ziffern). Es ist auch nicht eine 128 bit-Zahl, da 0xdeadbeef
nicht berührt wird, die im Einklang mit sizeof
Rückkehr 12.
Die Ausgabe für __int128
wie es aussieht ist wirklich nur ein 128-bit-Zahl. Alle bits schließlich umdrehen.
Kompilieren mit -m128bit-long-double
hat nicht ausrichten long double
zu 128 bit mit einer 32-bit-null Polsterung, wie dies durch die Dokumentation. Es nicht __int128
entweder, aber in der Tat scheint das ausrichten von 128 bits, padding mit dem Wert 0x7ffdd000
(?!).
Weiter LDBL_MAX
, scheint zu funktionieren, wie +inf
für beide long double
und __float128
. Addieren oder subtrahieren einer Zahl wie 1.0E100
oder 1.0E2000
an/aus LDBL_MAX
Ergebnisse in die gleiche bit-Muster.
Bis jetzt war es meine überzeugung, dass die foo_MAX
Konstanten wurden für die größte Darstellbare Zahl ist, die nicht +inf
(anscheinend ist das nicht der Fall?). Ich bin mir auch nicht ganz sicher, wie ein 80-bit-Zahl kann sich in der Praxis handeln, als +inf
für einen 128 bit Wert... vielleicht bin ich einfach zu müde am Ende des Tages, und etwas falsch gemacht haben.
LDBL_MAX
, haben Sie versucht, geteilt durch zwei? InformationsquelleAutor der Frage Damon | 2012-11-22
Du musst angemeldet sein, um einen Kommentar abzugeben.
Ad 1.
Diese Typen sind speziell für die Arbeit mit zahlen mit enormen Dynamikbereich. Die long double ist umgesetzt in einer einheitlichen Art und Weise, in der x87 FPU. Die Doppel-128b ich vermute umgesetzt würde im software-Modus auf modernen x86s, da es keine hardware zu tun, die Berechnungen in hardware.
Das lustige an der Sache ist, dass es durchaus üblich zu tun, viele floating-point-Operationen in einer Reihe und die Zwischenergebnisse werden nicht gespeichert in der Variablen deklariert, sondern gespeichert in FPU-Registern unter Ausnutzung der vollen Genauigkeit. Das ist, warum Vergleich:
Ist nicht sicher und kann nicht garantiert werden, um zu arbeiten (ohne zusätzlichen Schalter).
Ad. 3.
Gibt es einen Einfluss auf die Geschwindigkeit, je nachdem, was Präzision, die Sie verwenden. Sie können ändern, verwendet die Genauigkeit der FPU:
Wird es schneller sein, für kürzere Variablen, langsamer, länger. 128 bit verdoppelt wird wahrscheinlich mit software gemacht also viel langsamer.
Geht es nicht nur um den RAM-Speicher verschwendet, es geht um cache verschwendet. Gehen zu 80-bit-double aus 64b double-Abfall von 33% (32b) zu fast 50% (64b) des Speichers (inklusive cache).
Ad 4.
FPU und SSE-Einheiten sind völlig unterschiedlich. Sie können code verfassen, mit FPU gleichzeitig als SSE. Die Frage ist, was der compiler generiert, wenn Sie beschränken Sie die Verwendung nur SSE? Wird es versuchen, die FPU überhaupt? Ich habe seit einigen Programmierung mit SSE-und GCC erzeugen nur einzelne SISD auf seine eigenen. Sie haben zu helfen, es zu verwenden SIMD-Versionen. __float128 wird wohl an jeder Maschine arbeiten, auch die 8-bit AVR uC. Es ist nur das hantieren mit bits, nachdem alle.
Den 80 bit in hex-Darstellung ist tatsächlich 20 hex-Ziffern. Vielleicht sind die bits, die nicht benutzt werden von alten Betrieb? Auf meinem Rechner, die ich kompiliert den code und nur 20 bits ändern in langen
Modus: 66b4e0d2-ec09c1d5-00007ffe-deadbeef
Den 128-bit-version hat alle bits ändern. Blick auf die
objdump
es sieht aus, als wenn es mit software-emulation gibt es fast keine FPU Anweisungen.Scheint dies seltsam...
Ist es wahrscheinlich ausgebaut. Das Muster, das erkannt werden +inf in 80-bit wird übersetzt zu +inf 128-bit-float zu.
InformationsquelleAutor der Antwort Caladan
IEEE-754 definiert 32 und 64-Gleitkomma-Darstellungen für die Zwecke der effizienten Speicherung von Daten und einem 80-bit-Darstellung für die Zwecke der effizienten Berechnung. Die Absicht war, dass angesichts
float f1,f2; double d1,d2;
eine Aussage wied1=f1+f2+d2;
ausgeführt werden würde durch die Umwandlung der Argumente zu 80-bit-floating-point-Werte, indem Sie Sie, und konvertieren das Ergebnis zurück in eine 64-bit-floating-point-Typ. Dieser bietet drei Vorteile gegenüber der Durchführung von Operationen auf anderen floating-point-Typen-direkt:Während separate code oder schaltungen erforderlich wäre, für Konvertierungen in/aus 32-bit-Typen und 64-bit-Typen, wäre es nur notwendig, nur ein "add" - Implementierung, eine "multiplizieren" Implementierung "einer" Quadrat-Wurzel" - implementation, etc.
Obwohl in seltenen Fällen mit einem 80-bit-computational Art Ergebnisse hervorbringen, die waren sehr leicht weniger präzise als die Verwendung anderer Typen direkt (worst-case-Rundungsfehler ist 513/1024ulp in Fällen, in denen Berechnungen, die auf andere Arten ergeben würde ein Fehler 511/1024ulp), verkettete Berechnungen mit 80-bit-Typen, die Häufig genauer ist-manchmal viel mehr genau -, als Berechnungen mit anderen Arten.
Auf einem system ohne FPU, die Trennung ein
double
in einem separaten exponent und Mantisse vor der Durchführung der Berechnungen, die Normalisierung einer Mantisse, und Umwandlung eines separaten Mantisse und exponent in einerdouble
sind etwas zeitaufwändig. Wenn das Ergebnis einer Berechnung als Eingabe für eine andere und wieder verworfen, mit einem ausgepackt 80-bit-Typ zulassen, dass diese Schritte weggelassen werden.Damit dieser Ansatz der floating-point Mathematik, um nützlich zu sein, es ist jedoch wichtig, dass es möglich sein, den code zum speichern von Zwischenergebnissen mit der gleichen Präzision wie würde verwendet werden, in die Berechnung, so dass
temp = d1+d2; d4=temp+d3;
erzielen Sie dasselbe Ergebnis wied4=d1+d2+d3;
. Was ich sagen kann, der Zweck derlong double
war werden , Typ. Leider, auch wenn K&R-C ausgelegt, so dass alle floating-point-Werte übergeben werden, die Variable Methoden auf die gleiche Weise, ANSI-C brach. In C, wie Sie ursprünglich entworfen wurden, erhalten den codefloat v1,v2; ... printf("%12.6f", v1+v2);
, dieprintf
- Methode würden sich nicht darum zu sorgen, obv1+v2
würde die Ausbeute einesfloat
oder einedouble
, da das Ergebnis bekommen würde, gezwungen zu einem bekannten Typ unabhängig. Weiter, auch wenn die Art derv1
oderv2
geändertdouble
, dieprintf
- Anweisung nicht ändern.ANSI C, jedoch, erfordert, dass code, der Aufrufe
printf
müssen wissen, welche Argumentedouble
und dielong double
; eine Menge code--wenn nicht die Mehrheit--der code, der verwendetlong double
aber geschrieben wurde, auf Plattformen, wo es gleichbedeutend mitdouble
nicht den richtigen Formatbezeichner fürlong double
Werte. Anstattlong double
eine 80-bit-Typ, außer wenn übergeben als eine Variable Methode argument, in dem Fall wäre es gezwungen zu 64 bits, viele Compiler entschiedenlong double
gleichbedeutend mitdouble
und nicht bieten jede Art der Speicherung der Ergebnisse der intermediate-Berechnungen. Da die Verwendung eines extended-precision-Typ für die Berechnung ist nur gut, wenn diese Art der Programmierer, viele Menschen kamen zu dem Schluss hinsichtlich der erweiterten Präzision, wie das böse selbst war, obwohl es nur ANSI-C ' s Versagen zu handhaben Variable Argumente vernünftig gemacht, dass es problematisch.PS--Der beabsichtigte Zweck
long double
genutzt hätten, hätte es auch einlong float
wurde definiert als die Art, auf diefloat
Argumente könnten die meisten effizient gefördert; auf vielen Rechnern ohne floating-point-Einheiten, die wäre wahrscheinlich ein 48-bit-Typ, aber die optimale Größe könnte irgendwo im Bereich von 32 bits (auf Maschinen mit einer FPU, die nicht 32-bit-Mathematik direkt) bis zu 80 (auf Maschinen, bei denen das design vorgestellt, die durch IEEE-754). Zu spät jetzt, aber.InformationsquelleAutor der Antwort supercat
Läuft es auf den Unterschied zwischen 4.9999999999999999999 und 5.0.
InformationsquelleAutor der Antwort R Telkman