Forum: PC-Programmierung 8 Array Bytes to float64


von Michael (Gast)


Lesenswert?

Hallo,

ich überlege mir mittels eines 8Byte Array eine Gleitkommazahl zu 
übertragen.
Diese soll in einer 8Byte float Variablen unterkommen.
Das übertragende Array ist als unsigned int8 definiert.
Ich dachte mir durch das Bit Shiften die Daten byteweise rein zu 
schreiben,
nur bin ich ins Stocken geraten was das ablegen der Zahl in der float 
Variable angeht.
Die Gleitkommazahl wollte ich vor dem Verschicken mit 10000 
multiplizieren um diese als eine
Ganzzahl mittels Array zu übertragen.
1
  uint64 tempVar = 0;
2
  uint8  eightBitsProByte = 8; 
3
4
    for(uint8 i = 0; i < ((sizeof(uint64))); i++)
5
    {
6
        tempVar |= (dataArray[i] << (eightBitsProByte * ((sizeof(uint64) - 1) - i)));
7
    }
8
  
9
  finalVar = (float64)(tempVar / 10000); //<-- ich glaube hier laufe         //ich in Probleme rein,
10
  //da das berechnete Ergebnis zu gross wird für die float64 Variable.

Ist es komplett verkehrt, gibt es vllt. einen besseren Ansatz ?

Grüss
Michael

von Oliver S. (oliverso)


Lesenswert?

Äh, ja...

Vielleicht schreibst du dir mal im Detail auf, was wann und warum von wo 
nach wo übertragen werden soll, und was eine 8-Byte-Float Zahl sein 
soll, was eine Gleitkommazahl sein soll, wofür der Faktor 10000 gut sein 
soll, usw.

Und dann formulierst du deine Frage nochmals komplett neu.

Oliver

von cppbert (Gast)


Lesenswert?

4byte iee754 floating point => float (aka Single Precision)
8byte iee754 floating point => double (aka Double Precision)

hier bitte nur double oder float sagen sonst verwirrst du alle

keine Ahnung wofür du das * 10000 brauchst

wenn du garantieren kannst das beide System iee754 konforme floating 
points haben und du nicht drehen musst (little/big endian) dann kannst 
du einfach deine 8bytes rüberjagen und dort als double nutzen - nix mit 
konvertieren usw.

von Dirk B. (dirkb2)


Lesenswert?

Wenn deine Übertragung mit beliebigen Bitmustern zurecht kommt, dann 
kannst du einfach den Speicherbereich der double-Variablen übertragen.
Nix umwandeln, nix kopieren in int oder byte oder so.

Wenn die Übertragung extra Steuerzeichen braucht, ist es etwas 
aufwändiger.
Aber da bietet sich eine Konvertierung nach ASCII an.

von S. R. (svenska)


Lesenswert?

Mach das einfach so:
1
union blub {
2
    uint8_t bytes[8];
3
    double  wert;
4
};
5
6
void sende_float(double x)
7
{
8
    union blub test;
9
    test.wert = x;
10
    for(int i = 0; i < 8; i++)
11
        sende_byte(test.bytes[i]);
12
}

Und ja, das ist weder portabel noch vom Standard garantiert. Aber es 
funktioniert in der Realität. Besser als dein Bitgewürge ist es 
allemale.

von mh (Gast)


Lesenswert?

S. R. schrieb:
> Und ja, das ist weder portabel noch vom Standard garantiert. Aber es
> funktioniert in der Realität. Besser als dein Bitgewürge ist es
> allemale.

Warum die union mit undefined behafiour benutzen, wenn nen einfacher 
char Pointer funktioniert?
1
void sende_floatA(double x) {
2
    char const* p = (char const*)&x;
3
    for(int i = 0; i < 8; i++) {
4
        sende_byte(p[i]);
5
    }
6
}

Oder mit memcpy
1
void sende_floatB(double x) {
2
    char p[8];
3
    memcpy(p, &x, 8);
4
    for(int i = 0; i < 8; i++) {
5
        sende_byte(p[i]);
6
    }
7
}
gcc x86 compiliert beide Funktionen mit dem exakt gleichen Ergebnis.

von Dirk B. (dirkb2)


Lesenswert?

Eine Funktion sende_bytes(void *data, size_t len); sollte das alles 
machen können

Der Aufruf sende_bytes(&Variablenname, sizeof(Variablenname);

Man kann das auch hinter einem Makro verstecken oder noch mit 
sende_double kapseln.

Ohne kopieren oder unions.

: Bearbeitet durch User
von Dirk B. (dirkb2)


Lesenswert?

Hier noch der Code:
1
void sende_bytes(void *data, size_t len)
2
{ char *p = data;
3
4
  while(len-)
5
    sende_byte(*(p++));
6
}
Man kann noch den Rückgabewert von sende_byte, so vorhanden, auswerten

von Johannes O. (jojo_2)


Lesenswert?

Endianess bitte noch beachten. Generell ist das alles hier weiterhin 
undefined behavior. Jenachdem welche Maschinen beteiligt sind, ist das 
Ergebnis unterschiedlich.

von Dirk B. (dirkb2)


Lesenswert?

Es sollte nur übertragen werden.
Von (richtiger) Auswertung war nicht die Rede.

von mh (Gast)


Lesenswert?

Johannes O. schrieb:
> Endianess bitte noch beachten. Generell ist das alles hier weiterhin
> undefined behavior. Jenachdem welche Maschinen beteiligt sind, ist das
> Ergebnis unterschiedlich.

Das ist nicht die Bedeutung von undefined behaviour in c (oder c++)

Dirk B. schrieb:
> Eine Funktion sende_bytes(void *data, size_t len); sollte das alles
> machen können
>
> Der Aufruf sende_bytes(&Variablenname, sizeof(Variablenname);
>
> Man kann das auch hinter einem Makro verstecken oder noch mit
> sende_double kapseln.
>
> Ohne kopieren oder unions.
Die Schleife in eine Funktion auslagern ist natürliche der richtige 
Schritt. Über die Signatur, char oder void Pointer lässt sich allerdings 
streiten.

Ein sizeof hinter einem Makro verstecken ist allerdings keine gute Idee.

von Rolf M. (rmagnus)


Lesenswert?

mh schrieb:
> Die Schleife in eine Funktion auslagern ist natürliche der richtige
> Schritt. Über die Signatur, char oder void Pointer lässt sich allerdings
> streiten.

Eigentlich nicht. char* ist für Text, void* für Daten mit nicht genauer 
spezifiziertem Typ. Was passt hier wohl besser?

von mh (Gast)


Lesenswert?

Rolf M. schrieb:
> mh schrieb:
>> Die Schleife in eine Funktion auslagern ist natürliche der richtige
>> Schritt. Über die Signatur, char oder void Pointer lässt sich allerdings
>> streiten.
>
> Eigentlich nicht. char* ist für Text, void* für Daten mit nicht genauer
> spezifiziertem Typ. Was passt hier wohl besser?

Ich würde sagen "Pointer auf Bytes mit unspezifiziertem Inhalt" ist der 
richtige Datentyp. Und das ist in c ein char*. In der Funktion muss eh 
in einen char* gecasted werden, um auf die einzelnen Bytes zugreifen zu 
dürfen.

von Manfred M. (bittbeisser)


Lesenswert?

Ich habe noch nicht ganz verstanden, wofür genau das benötigt wird. Aber 
ich hatte mal ein ähnliches Problem. Da wollte ich ein double in eine 
Datei schreiben, um den Wert später, möglicherweise auf einem anderen 
Rechner, wieder einzulesen. Und das ganze natürlich ohne ein Bit an 
Genauigkeit zu verlieren.

Nach einer Diskussion in einem andere Forum bin ich dann beim sog. Hex 
Format des printf() Befehls gelandet:
1
snprintf( lbuf, 512, "    %22a%c // %15.8E    %d\n", val_b[i], lim, val_b[i], i );

Das erzeugt dann z.B. folgende Ausgaben:
1
double b_coeff[] = {
2
      0x1.707ee3c02f7b8p-3, //  1.79929523E-01    0
3
      0x1.707ee3c02f7b8p-2, //  3.59859046E-01    1
4
      0x1.707ee3c02f7b8p-3, //  1.79929523E-01    2
5
      0x1.b1186850d1e0ap-4, //  1.05736167E-01    3
6
      0x1.b1186850d1e0ap-3, //  2.11472335E-01    4
7
      0x1.b1186850d1e0ap-4, //  1.05736167E-01    5
8
      0x1.731c8178b6158p-5, //  4.53016785E-02    6
9
      0x1.731c8178b6158p-4, //  9.06033571E-02    7
10
      0x1.731c8178b6158p-5  //  4.53016785E-02    8
11
};
atof() kann eine solche Zahl lesen.

Vielleicht hilft das um zu einer Lösung zu kommen.

von Rolf M. (rmagnus)


Lesenswert?

mh schrieb:
> Rolf M. schrieb:
>> mh schrieb:
>>> Die Schleife in eine Funktion auslagern ist natürliche der richtige
>>> Schritt. Über die Signatur, char oder void Pointer lässt sich allerdings
>>> streiten.
>>
>> Eigentlich nicht. char* ist für Text, void* für Daten mit nicht genauer
>> spezifiziertem Typ. Was passt hier wohl besser?
>
> Ich würde sagen "Pointer auf Bytes mit unspezifiziertem Inhalt" ist der
> richtige Datentyp.

Und wofür siehst du void* als den richtigen Typ an, wenn nicht für 
sowas? Das ist genau für unspezifizierten Inhalt gemacht. Dass es Bytes 
sind, ist logisch. Daraus ist alles im Speicher aufgebaut.

> Und das ist in c ein char*.

Wie gesagt: char* sollte man für Text verwenden, und sonst für nichts. 
Wenn schon, dann wenigstens z.B. unsigned char*.

> In der Funktion muss eh in einen char* gecasted werden, um auf die einzelnen
> Bytes zugreifen zu dürfen.

Das ist richtig, aber ändert nichts daran. Die Parameterliste einer 
Funktion ist im Prinzip die Definition einer Schnittstelle, und der Typ 
sollte möglichst gut zum Ausdruck bringen, was erwartet wird. Und für 
mich  steht sowas wie char* für Text und unsigned char* für "ein Zeiger 
auf einen kleinen vorzeichenlosen Integer" und nicht für "Zeiger auf 
irgendwas unspezifisches".

: Bearbeitet durch User
von mh (Gast)


Lesenswert?

Für mich ist void* ein Pointer auf unbekannten Typ.

Rolf M. schrieb:
> Die Parameterliste einer
> Funktion ist im Prinzip die Definition einer Schnittstelle, und der Typ
> sollte möglichst gut zum Ausdruck bringen, was erwartet wird.

Die Funktion, um die es geht, erwartet Bytes, da sie Bytes verschicken 
soll. Also sollten Bytes als Typ in der Parameterliste stehen. In C ist 
der Typ für Bytes char, da es keinen expliziten Typ dafür gibt. Der 
Standard hat extra Ausnahmen für character types. C++ hat für diesen 
Zweck zum Glück endlich std::byte.

Void* steht für einen unbekannten oder unbestimmten Typ. Ein Beispiel 
dafür ist der Pointer auf Nutzerdaten in einem Callback. An der Stelle 
ist egal worauf der Pointer zeigt, solange die Funktion, die aufgerufen 
wird, um welchen konkreten Typ es geht.

Rolf M. schrieb:
> Wenn schon, dann wenigstens z.B. unsigned char*.

Warum? Du sagst selbst

Rolf M. schrieb:
> unsigned char* für "ein Zeiger
> auf einen kleinen vorzeichenlosen Integer"

Was ist die Bedeutung von "Vorzeichen" bei einem "Byte"? Das ist die 
gleiche Frage wie was ist ein "Vorzeichen" bei einem "Buchstaben"?

von Rolf M. (rmagnus)


Lesenswert?

mh schrieb:
> Für mich ist void* ein Pointer auf unbekannten Typ.
>
> Die Funktion, um die es geht, erwartet Bytes, da sie Bytes verschicken
> soll.

Sie erwartet einen Speicherblock mit unbekanntem Inhalt.

> Void* steht für einen unbekannten oder unbestimmten Typ. Ein Beispiel
> dafür ist der Pointer auf Nutzerdaten in einem Callback. An der Stelle
> ist egal worauf der Pointer zeigt, solange die Funktion, die aufgerufen
> wird, um welchen konkreten Typ es geht.

Aber genau das ist doch hier auch der Fall: Ein Sender will Nutzerdaten 
eines der Sendefunktion unbekannten Typs zu einem Empfänger schicken. 
Für die Übertragung ist es egal, worauf er zeigt, solange sich die 
Nutzer der Übertraungsfunktionen einig über den Typ sind.

> Rolf M. schrieb:
>> Wenn schon, dann wenigstens z.B. unsigned char*.
>
> Warum? Du sagst selbst
>
> Rolf M. schrieb:
>> unsigned char* für "ein Zeiger
>> auf einen kleinen vorzeichenlosen Integer"

Ja, ich halte unsigned char* ja auch für den falschen Typ. Aber er ist 
wenigstens besser als nur char*. Schon alleine, weil dann der Bit-Shift 
nicht Gefahr läuft, Mist zu produzieren. Denn bei char ist nicht 
festgelegt, ob er vorzeichenbehaftet ist oder nicht.

> Was ist die Bedeutung von "Vorzeichen" bei einem "Byte"?

unsigned char ist nicht einfach nur "ein Byte", sondern ein Integer, der 
ein Byte groß ist. Er hat einen Zahlenwert im Bereich 0 bis 255 
(mindestens). Deshalb ist es ja eigentlich der falsche Typ.

> Das ist die  gleiche Frage wie was ist ein "Vorzeichen" bei einem
> "Buchstaben"?

Deshalb nimmt man für Text (und nur dafür) char. Da ist wie gesagt nicht 
spezifiziert, ob der ein Vorzeichen hat oder nicht, weil das bei der 
Nutzung für Text überhaupt keine Rolle spielt. Und ja, es wäre schöner 
gewesen, wenn die Unterscheidung zwischen Integern und Zeichen in C 
etwas stärker ausgeprägt wäre, was sie aber leider nicht ist.

von Dirk B. (dirkb2)


Lesenswert?

Der interne Typ muss zum Parameter der Funktion sende_bytes passen.

Da der Wert nur durchgereicht und nicht bearbeitet wird, ist der genaue 
Typ in diesem Beispiel eigentlich egal.

von mh (Gast)


Lesenswert?

Rolf M. schrieb:
> mh schrieb:
>> Für mich ist void* ein Pointer auf unbekannten Typ.
>>
>> Die Funktion, um die es geht, erwartet Bytes, da sie Bytes verschicken
>> soll.
>
> Sie erwartet einen Speicherblock mit unbekanntem Inhalt.
Genau! Kein unbekannter Typ, sondern Bytes im Speicher mit unbekanntem 
Inhalt.

Rolf M. schrieb:
> Für die Übertragung ist es egal, worauf er zeigt, solange sich die
> Nutzer der Übertraungsfunktionen einig über den Typ sind.
Die Nutzer müssen sich noch um einiges mehr als den Typ gedanken machen. 
Zum Beispiel wie genau das Objekt im Speicher liegt.


Rolf M. schrieb:
> Schon alleine, weil dann der Bit-Shift
> nicht Gefahr läuft, Mist zu produzieren. Denn bei char ist nicht
> festgelegt, ob er vorzeichenbehaftet ist oder nicht.
Das ist tatsächlich ein Argument für unsigned.

Rolf M. schrieb:
> unsigned char ist nicht einfach nur "ein Byte", sondern ein Integer, der
> ein Byte groß ist. Er hat einen Zahlenwert im Bereich 0 bis 255
> (mindestens).
Zustimmung.

Rolf M. schrieb:
> Deshalb nimmt man für Text (und nur dafür) char. Da ist wie gesagt nicht
> spezifiziert, ob der ein Vorzeichen hat oder nicht, weil das bei der
> Nutzung für Text überhaupt keine Rolle spielt.
Für ein "Byte" im Speicher gilt genau das gleiche. Da es nur für 
character types die nötigen Ausnahmen für den "bytewisen" Zugriff auf 
Objekte im Speicher gibt, gibt es damit keine Alternative als char für 
diesen Zweck.

von Rolf M. (rmagnus)


Lesenswert?

mh schrieb:
> Rolf M. schrieb:
>> mh schrieb:
>>> Für mich ist void* ein Pointer auf unbekannten Typ.
>>>
>>> Die Funktion, um die es geht, erwartet Bytes, da sie Bytes verschicken
>>> soll.
>>
>> Sie erwartet einen Speicherblock mit unbekanntem Inhalt.
> Genau! Kein unbekannter Typ, sondern Bytes im Speicher mit unbekanntem
> Inhalt.

Das ist für mich das selbe. Daten jedes beliebigen Datentyps bestehen 
aus Bytes im Speicher. Und ich sehe auch nicht, wo im Bezug auf die 
Übergabe per Paramter der große Unterschied sein soll, ob ich die 
Adresse eines double an eine Callback-Funktion oder an eine 
Sende-Funktion übergebe. Warum soll das eine einen void*, das andere 
aber einen char* bekommen?
malloc() allokiert auch Bytes im Speicher und gibt einen void* darauf 
zurück. Und die Funktion fwrite() will einen void*, der auf die Bytes 
zeigt, die es auf die Festplatte rausschreiben soll.

> Rolf M. schrieb:
>> Für die Übertragung ist es egal, worauf er zeigt, solange sich die
>> Nutzer der Übertraungsfunktionen einig über den Typ sind.
> Die Nutzer müssen sich noch um einiges mehr als den Typ gedanken machen.
> Zum Beispiel wie genau das Objekt im Speicher liegt.

Das ist aber unabhängig davon, welcher Typ an die Sende-Funktionen 
übergeben wird.

> Rolf M. schrieb:
>> unsigned char ist nicht einfach nur "ein Byte", sondern ein Integer, der
>> ein Byte groß ist. Er hat einen Zahlenwert im Bereich 0 bis 255
>> (mindestens).
> Zustimmung.

Und was repräsentiert dann dieser Wert? Für sich genommen gar nichts 
brauchbares, weil es eben kein kleiner Integer, sondern einfach nur ein 
Teil eines an der Stelle unbekannten Typ ist.

> Für ein "Byte" im Speicher gilt genau das gleiche. Da es nur für
> character types die nötigen Ausnahmen für den "bytewisen" Zugriff auf
> Objekte im Speicher gibt, gibt es damit keine Alternative als char für
> diesen Zweck.

Intern für den Zugriff ja. Der ist aber ein Implementierungs-Detail. Die 
Schnittstelle sollte sich nicht daran orientieren, wie die Funktion 
intern ihre Aufgabe erledigt.

von Dirk B. (dirkb2)


Lesenswert?

Der Typ ist für die Übertragung (an dieser Stelle) völlig unwichtig.

Darum wurde ein Typ gewählt, der leicht in andere Pointer gewandelt 
werden kann.
Dies trifft für void* (zumindest bei C) zu.

Ein Problem vom TO ist, dass er unbedingt ein double in ein char[8] 
konvertieren wollte, da er nicht verstanden hat, dass dies der 
Übertragungsroutine völlig egal ist.

Auch sowas kann man mit void* vermeiden.

Man kann die Funktion auch sende_daten nennen, damit sich keiner mehr an 
den bytes aufge..t.

von mh (Gast)


Lesenswert?

Rolf M. schrieb:
> Und was repräsentiert dann dieser Wert? Für sich genommen gar nichts
> brauchbares, weil es eben kein kleiner Integer, sondern einfach nur ein
> Teil eines an der Stelle unbekannten Typ ist.

Was repräsentiert der void*, der auf der anderen Seite von deiner 
empfange_bytes Funktion zurückgegeben wird?

Rolf M. schrieb:
> malloc() allokiert auch Bytes im Speicher und gibt einen void* darauf
> zurück. Und die Funktion fwrite() will einen void*, der auf die Bytes
> zeigt, die es auf die Festplatte rausschreiben soll.
Ich bin mir nicht sicher, ob die API der cstdlib an dieser Stelle ein 
gutes Beispiel ist. Wenn du noch das, an dieser Stelle naheliegende, 
memcpy dazu nimmst, hast du drei Funktionen, die direkt mit den Bytes im 
Speicher arbeiten, und jeweils ziemlich unterschiedliche Parameter dafür 
nutzen. Nur size, oder doch size und count? Erst src und dann dst oder 
doch lieber dst und dann src? Dann sagt der Standard zu fwrite:
1
For each object, size calls are made to the fputc function, taking the values (in order) from an array of unsigned char exactly overlaying the object.
und zu memcpy:
1
The memcpy function copies n characters from the object pointed to by s2 into the object pointed to by s1.
Die ganzen Funktionen, die "Buchstaben" verarbeiten, aber int als Typ 
benutzen, liste ich mal lieber nicht auf.
Weil das ganze Spass macht nehme ich auch noch memset:
1
The memset function copies the value of c (converted to an unsigned char) into
2
each of the first n characters of the object pointed to by s.
Der Parameter c hat natürlich den Typ int.

Dirk B. schrieb:
> Man kann die Funktion auch sende_daten nennen, damit sich keiner mehr an
> den bytes aufge..t.
Das ist natürlich die Lösung...

von Rolf M. (rmagnus)


Lesenswert?

mh schrieb:
> Rolf M. schrieb:
>> Und was repräsentiert dann dieser Wert? Für sich genommen gar nichts
>> brauchbares, weil es eben kein kleiner Integer, sondern einfach nur ein
>> Teil eines an der Stelle unbekannten Typ ist.
>
> Was repräsentiert der void*, der auf der anderen Seite von deiner
> empfange_bytes Funktion zurückgegeben wird?

Nichts spezifisches. Deshalb ja void. Genau dafür ist void* da. Alle 
anderen suggerieren Daten von einem bestimmten Typ, auch char. Der 
Aufrufer der Empfangsfunktion muss dann wissen, was dahinter steckt (wie 
eben z.B. ein double) und das in diesen Typ casten. Die Empfangsfunktion 
selber weiß das aber nicht. Sie bekommt einfach einen Zeiger auf 
"irgendwas"(=void), wo sie die Daten ablegen kann.

> Ich bin mir nicht sicher, ob die API der cstdlib an dieser Stelle ein
> gutes Beispiel ist.

Von mir aus kannst du auch bei POSIX schauen. Die Funktion write() will 
beispielsweise einen const void* haben. Die Funktion mmap() einen void*. 
shmat() auch. Oder die WinAPI. WriteFile() nimmt einen LPCVOID, was 
Microsofts "fancy"-Spezialversion eines const void* ist. Oder FreeRTOS. 
xStreamBufferSend() will auch einen const void*.
Es ist einfach der Standardfall, dass für sowas void* zum Einsatz kommt.

> Wenn du noch das, an dieser Stelle naheliegende, memcpy dazu nimmst, hast du
> drei Funktionen, die direkt mit den Bytes im Speicher arbeiten, und jeweils
> ziemlich unterschiedliche Parameter dafür nutzen. Nur size, oder doch size
> und count?

Aber immer void*. Wozu size und count, habe ich tatsächlich auch nie 
verstanden.

> Die ganzen Funktionen, die "Buchstaben" verarbeiten, aber int als Typ
> benutzen, liste ich mal lieber nicht auf.

C nutzt für einzelne Zeichen konsistent immer int als Typ. Soweit ich 
weiß, um bei Funktionen, die das unterstützen, auch EOF separat 
darstellen zu können. Es wird aber auch bei den Funktionen durchgezogen, 
wo's kein EOF gibt.

> Weil das ganze Spass macht nehme ich auch noch memset:The memset
> function copies the value of c (converted to an unsigned char) into
> each of the first n characters of the object pointed to by s.
> Der Parameter c hat natürlich den Typ int.

memset gehört in C anscheinend auch zu dein Stringfunktionen, warum auch 
immer. Daher wird - wie eben bei allem, wo auch einzelne Zeichen 
vorkommen - int verwendet. Ist ja immerhin auch in <string.h> 
deklariert.

: Bearbeitet durch User
von mh (Gast)


Lesenswert?

Mir ist bewusst, warum die Funktionen int als Typ benutzen, und ich 
finde die Begründung mangelhaft. Um das Warum geht es an dieser Stelle 
auch nicht. Mein Punkt ist, dass ich deiner Meinung
Rolf M. schrieb:
> Deshalb nimmt man für Text (und nur dafür) char.
nicht zustimmen kann, und der c-Standard als Gegenbeispiel dient.

von Dirk (Gast)


Lesenswert?

Und was ist nun die Lösung zur Frage?

von Rolf M. (rmagnus)


Lesenswert?

Es wurden doch mehrere mögliche Lösungen genannt. Such dir halt eine 
aus.

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.