Forum: PC-Programmierung Manueller Typ-Cast (C/C++)


von Zisko (Gast)


Lesenswert?

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

von Karl H. (kbuchegg)


Lesenswert?

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.

von Zisko (Gast)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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
von Yalu X. (yalu) (Moderator)


Lesenswert?

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
von Mark B. (markbrandis)


Lesenswert?

Yalu X. schrieb:
> Ich habe mal Konvertierungroutinen von double nach half und half nach
> double geschrieben

Ich liebe ja einbuchstabige Variablennamen total ;-)

von Yalu X. (yalu) (Moderator)


Lesenswert?

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 ;-)

von Mark B. (markbrandis)


Lesenswert?

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
von Yalu X. (yalu) (Moderator)


Lesenswert?

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
Noch kein Account? Hier anmelden.