Hallo zusammen, ich suche nach einer Erklärung oder einem Beispiel wie der Typecast double -> float vollzogen wird. Leider war meine google-Suche da nicht sehr ergiebig. Wahrscheinlich suche ich nach den falschen Schlüsselwörtern. Hintergrund meiner Frage: Ich möchte dieses Cast-Verfahren als Vorlage für einen Typecast von float auf ein 16 Bit Floatingpoint-Format (1 Bit Vorzeichen, 5 Bit Exponent, 10 Bit Mantisse) anwenden und das Rad nicht neu erfinden. Viele Grüße Zisko
Zisko schrieb: > Hallo zusammen, > > ich suche nach einer Erklärung oder einem Beispiel wie der Typecast > double -> float vollzogen wird. Leider war meine google-Suche da nicht > sehr ergiebig. Wahrscheinlich suche ich nach den falschen > Schlüsselwörtern. Nicht nur das. Das wird dir auch nicht viel helfen. Denn im Grunde besteht der cast bei den üblichen Floating Point Formaten nur darin, solange in der Mantisse (beginnend mit den niederwertigsten Bits), Bits wegzuwerfen, bis die geforderte Anzahl an Bits übrig bleibt. Beim Exponenten wird im Prinzip auch nicht anders verfahren, nur das diesmal am MOst signifikant Bit Ende angefangen wird, wenn float und double eine unterschiedliche Anzahl an Exponentenbits benutzen. D.h. man könnte die Operation auch so beschreiben: Das ist nichts anderes als ein Umkopieren von einem Speicherbereich in einen anderen, wobei nur ausgewählte Teile des Original mitgenommen werden. > Hintergrund meiner Frage: Ich möchte dieses Cast-Verfahren als Vorlage > für einen Typecast von float auf ein 16 Bit Floatingpoint-Format (1 Bit > Vorzeichen, 5 Bit Exponent, 10 Bit Mantisse) anwenden und das Rad nicht > neu erfinden.
Ergo nehme ich beim cast double -> float die ersten 23 Bit der Mantisse und die ersten 8 bit des Exponenten, pack das Vorzeichen hinzu und lege alles auf 32 Bit Speicher ab, wenn ich das richtig verstanden habe.
Zisko schrieb: > Ergo nehme ich beim cast double -> float ... > die ersten 8 bit des Exponenten die letzten 8 Bit Ein "2 hoch 12" ist auch als float "2 hoch 12"
:
Bearbeitet durch User
Ich habe mal Konvertierungroutinen von double nach half und half nach double geschrieben (natürlich ohne Garantie für Korrektheit):
1 | #include <stdio.h> |
2 | #include <stdlib.h> |
3 | #include <stdint.h> |
4 | #include <math.h> |
5 | |
6 | // Datentyp für 16-Bit-Floats
|
7 | typedef uint16_t half; |
8 | |
9 | // Bitzahl für Exponent und Mantisse
|
10 | #define DE_BITS 11
|
11 | #define DM_BITS 52
|
12 | |
13 | #define HE_BITS 5
|
14 | #define HM_BITS 10
|
15 | |
16 | // Exponenten-Bias (s. IEEE-754)
|
17 | #define DE_BIAS ((1 << (DE_BITS-1)) - 1)
|
18 | #define HE_BIAS ((1 << (HE_BITS-1)) - 1)
|
19 | |
20 | // Konvertierung von double nach half
|
21 | half d2h(double d) { |
22 | uint64_t id = *(uint64_t *)&d, m; |
23 | int s, e; |
24 | half h; |
25 | |
26 | // Vorzeichen, Exponent und Mantisse extrahieren
|
27 | s = id >> (DE_BITS + DM_BITS); |
28 | e = id >> DM_BITS & ((1 << DE_BITS) - 1); |
29 | m = id & (((uint64_t)1 << DM_BITS) - 1); |
30 | |
31 | |
32 | // Exponent (mit Bereichsabfrage) und Mantisse (mit Runden) neu skalieren
|
33 | if (e) { |
34 | e += HE_BIAS - DE_BIAS; |
35 | m = (m + ((uint64_t)1 << (DM_BITS - HM_BITS - 1))) >> (DM_BITS - HM_BITS); |
36 | if (m >= 1 << HM_BITS) { |
37 | m = 0; |
38 | e++; |
39 | }
|
40 | if (e <= 0) |
41 | m = e = 0; |
42 | else if (e >= 1<<HE_BITS) |
43 | printf("Exponent zu klein oder zu groß\n"); |
44 | }
|
45 | else
|
46 | m = 0; |
47 | |
48 | // Vorzeichen, Exponent und Mantisse zu einer 16-Bit-Zahl zusammensetzen
|
49 | h = (s << HE_BITS | e) << HM_BITS | m; |
50 | |
51 | return h; |
52 | }
|
53 | |
54 | // Konvertierung von half nach double
|
55 | double h2d(half h) { |
56 | uint64_t id, m; |
57 | int s, e; |
58 | |
59 | // Vorzeichen, Exponent und Mantisse extrahieren
|
60 | s = h >> (HE_BITS + HM_BITS); |
61 | e = h >> HM_BITS & ((1 << HE_BITS) - 1); |
62 | m = h & ((1 << HM_BITS) - 1); |
63 | |
64 | // Exponent und Mantisse neu skalieren
|
65 | if (e) { |
66 | e += DE_BIAS - HE_BIAS; |
67 | m <<= DM_BITS - HM_BITS; |
68 | }
|
69 | else
|
70 | m = 0; |
71 | |
72 | // Vorzeichen, Exponent und Mantisse zu einer 64-Bit-Zahl zusammensetzen
|
73 | id = ((uint64_t)s << DE_BITS | e) << DM_BITS | m; |
74 | |
75 | return *(double *)&id; |
76 | }
|
77 | |
78 | int main(int argc, char *argv[]) { |
79 | double d = strtod(argv[1], NULL); |
80 | half h; |
81 | |
82 | // double nach half konvertieren ...
|
83 | h = d2h(d); |
84 | |
85 | // ... und wieder zurück
|
86 | d = h2d(h); |
87 | |
88 | printf("%g\n", d); |
89 | |
90 | return 0; |
91 | }
|
Dabei folgt die Anordnung und Darstellung der Zahlenbestandteile (Vorzeichen, Exponent und Mantisse) dem Standard IEEE-754: http://de.wikipedia.org/wiki/IEEE_754 Denormalisierte Zahlen, Unendlich und NaN sind nicht implementiert. Dafür ist das Bitmuster 11111 für den Exponenten erlaubt, was den Wertebereich für große Zahlen etwas erweitert. Er reicht von ±6.10352e-05 bis ±1.31008Ee+05. Zusätzlich gibt es noch die Werte ±0. Die Darstellungsgenauigkeit beträgt 3 bis 4 Stellen. Bei der Konvertierung von double nach half kann es passieren, dass der Exponent nicht darstellbar ist. Ist er zu klein, wird auf ±0 gerundet. Ist er zu groß, wird eine Fehlermeldung ausgegeben. Man könnte an dieser Stelle auch eine Exception auslösen oder Unendlich liefern, wenn dieses implementiert wäre. Wenn es auf Laufzeiteffizienz ankommt, kann der Code sicher noch etwas optimiert werden. So können bspw. die Shifts für die Extraktion der Zahlenbestandteile mit denen beim Wiederzusammensetzen der Zahl zusammengefasst werden. Edit: Hab gerade noch einen Bug gefixt :)
:
Bearbeitet durch Moderator
Yalu X. schrieb: > Ich habe mal Konvertierungroutinen von double nach half und half nach > double geschrieben Ich liebe ja einbuchstabige Variablennamen total ;-)
Mark Brandis schrieb: > Ich liebe ja einbuchstabige Variablennamen total ;-) Ich bin etwas schreibfaul und dachte, wenigstens diese Kürzel sollten klar sein: s = sign e = exponent m = mantissa h = half d = double Aber wohl doch nicht ;-)
Yalu X. schrieb: > Ich bin etwas schreibfaul und dachte, wenigstens diese Kürzel sollten > klar sein: > > s = sign > e = exponent > m = mantissa > h = half > d = double > > Aber wohl doch nicht ;-) Nun ja, die Kommentare sind auf deutsch, die Variablennamen kürzen die englischen Begriffe ab... find ich jetzt nicht unbedingt ideal. Auch wenn es hier nur beim Vorzeichen einen Unterschied macht. Ich seh halt so einen Deutsch/Englisch Mischmasch im Code auf der Arbeit jeden Tag, und wesentlich schlimmer als hier... wahrscheinlich hängt's mir deshalb schon zum Hals raus. ;-)
:
Bearbeitet durch User
Mark Brandis schrieb: > Nun ja, die Kommentare sind auf deutsch, die Variablennamen kürzen die > englischen Begriffe ab... find ich jetzt nicht unbedingt ideal. Auch > wenn es hier nur beim Vorzeichen einen Unterschied macht. Oh Mann, ich habe die Bezeichnungen einfach aus dem (deutschsprachigen) Wikipedia-Artikel übernommen, zu dem ich schon oben den Bezug hergestellt habe: http://de.wikipedia.org/wiki/IEEE_754 Du kannst die Diskussion über lange/kurze und deutsche/englische Variablennamen gerne dort fortführen. Wenn ich dann sehe, dass sie dort im gegenseitigen Einvernehmen geändert worden sind, werde ich diese Änderungen auch in meinen obigen Code einfließen lassen ;-)
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.