Für eine Umwandlung von DEC Zahlen in das ASCII Format muss ich nach für die Anzahl der Stellen immer durch 10 teilen. Ich arbeite mit einem Atmega32u4, dieser hat ja nun auch keine Hardwaredivision. Gibt es eine bessere Alternative als ein x/10? Vielleicht ein bitshift mit aufaddieren?
Draco schrieb: > Für eine Umwandlung von DEC Zahlen in das ASCII Format muss ich nach für > die Anzahl der Stellen immer durch 10 teilen wieso das denn? itoa ltoa ultoa dtostr wäre eine Möglichkeit, sprintf eine andere
Draco schrieb: > Gibt es eine > bessere Alternative als ein x/10? Vielleicht ein bitshift mit > aufaddieren? Zumindest hier: http://stackoverflow.com/questions/5558492/divide-by-10-using-bit-shifts findet man eine Shift-Add-Variante. Aber ob die tatsächlich schneller ist?
Draco schrieb: > dieser hat ja nun auch keine Hardwaredivision. Na und? Wieviel Millionen ASCII-Zeichen mußt Du denn je Sekunde ausgeben?
Überlass das mal dem Compiler, der wird schon guten Code erzeugen. Für a=b/2 und a=b<<1 erzeugt der Compiler exakt den selben Bytecode.
Joachim B. schrieb: > wieso das denn? > > itoa ltoa ultoa dtostr wäre eine Möglichkeit, sprintf eine andere Frank M. schrieb: > findet man eine Shift-Add-Variante. Aber ob die tatsächlich schneller > ist? Peter D. schrieb: > Na und? > Wieviel Millionen ASCII-Zeichen mußt Du denn je Sekunde ausgeben? Es geht nicht um Geschwindigkeitsvorteile, es geht um Platzersparnis. Frank M. schrieb: > Zumindest hier: > > http://stackoverflow.com/questions/5558492/divide-by-10-using-bit-shifts Vielen Dank, das sieht vielversprechend aus, das schaue ich mir mal an!
Rene K. schrieb: > Es geht nicht um Geschwindigkeitsvorteile, es geht um Platzersparnis. Beitrag "Re: 8bis32bit binär in Dezimal ausgeben"
Draco schrieb: > Für eine Umwandlung von DEC Zahlen in das ASCII Format muss ich nach für > die Anzahl der Stellen immer durch 10 teilen. Ich arbeite mit einem > Atmega32u4, dieser hat ja nun auch keine Hardwaredivision. Gibt es eine > bessere Alternative als ein x/10? Ich würde vermuten, der Compiler kann die Division durch die Konstante so gut optimieren, dass jede andere Lösung umständlicher wäre. > Vielleicht ein bitshift mit aufaddieren? Das geht nur dann efizient, wenn du beliebigen Zugriff auf die einzelnen Bits hast. Also: nimm ein FPGA. http://www.lothar-miller.de/s9y/categories/44-BCD-Umwandlung Frank M. schrieb: > findet man eine Shift-Add-Variante. Aber ob die tatsächlich schneller ist? Sicher nicht auf einem Rechenknecht, der nur 8 Bit kann und dann schon wieder mit einem Übertrag rumhampeln muss und keinen Nibble-Zugriff beherrscht...
Stefan U. schrieb: > Für a=b/2 und a=b<<1 erzeugt der Compiler exakt den selben Bytecode. Bestimmt nicht. Ersteres ist eine Division, letzteres eine Plutimikation ;-)
>> Für a=b/2 und a=b<<1 erzeugt der Compiler exakt den selben Bytecode. > Bestimmt nicht. Sorry, die Pfeile sind natürlich falsch herum.
>> Allenfalls zu beginn weg in BCD rechnen ? > >AVR's haben doch gar keine BCD Operationen. So schwierig ist das nun auch wieder nicht.
>>> Allenfalls zu beginn weg in BCD rechnen ? >> AVR's haben doch gar keine BCD Operationen. > So schwierig ist das nun auch wieder nicht. Aber es macht doch keinen Sinn, Daten im BCD Format zu speichern, wenn es keine dazu passenden Operationen gibt. Dann müsste man doch vor und nach jeder Operation konvertieren. Damit gewinnt der TO nichts.
>> bessere Alternative als ein x/10? > x * 0,1 Ach komm schon, jetzt wird's aber schäbig.
Sooo.. ganz dämlich und einfach - einfach ne Multiplikation mit 0.1:
1 | uint16_t mul01(uint16_t divisior) |
2 | {
|
3 | return divisior * 0.1; |
4 | }
|
5 | |
6 | void own_utoa( uint16_t uIntValue, char* Buffer ) |
7 | {
|
8 | uint8_t i = 0; |
9 | char temp; |
10 | |
11 | while( uIntValue > 0 ) |
12 | {
|
13 | Buffer[i++] = '0' + (uIntValue % 10); |
14 | uIntValue = mul01(uIntValue); // Hierfür!!! |
15 | }
|
16 | |
17 | |
18 | for( uint8_t j = 0; j < i / 2; ++j ) |
19 | {
|
20 | temp = Buffer[j]; |
21 | Buffer[j] = Buffer[i-j-1]; |
22 | Buffer[i-j-1] = temp; |
23 | }
|
24 | Buffer[i] = '\0'; |
25 | }
|
Das hat den Sourcecode um satte 500 Bytes minimiert bei nur einer 16Bit Ausgabe (Dec: 786). Mit mul01(uIntValue) :
1 | Program Memory Usage : 4508 bytes 13,8 % Full |
2 | Data Memory Usage : 70 bytes 2,7 % Full |
Mit uIntValue /= 10 :
1 | Program Memory Usage : 5072 bytes 15,5 % Full |
2 | Data Memory Usage : 70 bytes 2,7 % Full |
Eine Multiplikation mit Ganzzahlen und Bitshift hat nicht so den erhofften Erfolg gebracht ((divisior * 205) >> 11;) - der dort mit 32bit gerechnet werden muss um kein Overflow herbeizuführen, bringt nicht sonderliche Vorteile und ist auf 1024 begrenzt:
1 | Program Memory Usage : 4506 bytes 13,8 % Full |
2 | Data Memory Usage : 70 bytes 2,7 % Full |
Stefan U. schrieb: >>> bessere Alternative als ein x/10? >> x * 0,1 > > Ach komm schon, jetzt wird's aber schäbig. OK ganzzahlig *10/100 (scnr) Rene K. schrieb: > Es geht nicht um Geschwindigkeitsvorteile, es geht um Platzersparnis. nächst größeren nehmen, wozu um Cent zu sparen zu lange am Code sitzen?
Joachim B. schrieb: > nächst größeren nehmen, wozu um Cent zu sparen zu lange am Code sitzen? In der u4 Reihe ist der Mega32 der größte IMHO.
isn Argument, bis jetzt habe ich lieber nach m328p den m1284p genommen, den m32u4 muss ich erst noch kennenlernen, liegt schon hier rum.
Joachim B. schrieb: > isn Argument, bis jetzt habe ich lieber nach m328p den m1284p genommen, > den m32u4 muss ich erst noch kennenlernen, liegt schon hier rum. Ist nen feines Teil mit dem "On-Board" USB - wenn man da was größeres will muss man in der AT90USB Reihe rumstöbern. Das ist dann aber leider nimmer Pinkompatibel.
Rene K. schrieb: > Es geht nicht um Geschwindigkeitsvorteile, es geht um Platzersparnis. Ich habs grad mal compiliert, itoa() benötigt wahnsinnig enorme, extrem riesige, unvorstellbar gigantische 134 Byte Flash, das sind volle 0,4% Deines ATmega32. Findest Du es nicht selber lachhaft, darum so ein Getöse zu machen?
Peter D. schrieb: > Ich habs grad mal compiliert, itoa() benötigt wahnsinnig enorme, extrem > riesige, unvorstellbar gigantische 134 Byte Flash, das sind volle 0,4% > Deines ATmega32. > Findest Du es nicht selber lachhaft, darum so ein Getöse zu machen? Das Argument lasse ich gelten :-D Leider muss ich viele Werte umwandeln um sie via UART rauszuschicken. In der Summierung macht sich das schon bezahlbar ja. Und "Getöse" war es ja nichtmal, ging ja alles flott von der Hand und hat mich ja auch zeitlich nicht sonderlich aufgehalten. Das andere wäre ja auch noch eine Spur einfacher gewesen, ich könnte ja auch die Daten direkt rausschicken und auf dem Host die Umrechnung machen lassen. Aber so isses schöner, passt, is kleiner und läuft.
Rene K. schrieb: > Leider muss ich viele Werte umwandeln um sie via UART rauszuschicken. > In der Summierung macht sich das schon bezahlbar ja. Dir ist aber schon klar, dass itoa() eine Funktion ist, die nur einmal im Flash gespeichert werden muss, oder?
Rene K. schrieb: > In der Summierung macht sich das schon > bezahlbar ja. Nein, eben nicht. Die Routine wird nur einmal angelegt, egal wie oft Du sie aufrufst. Um die Aufrufkosten zu senken, hilft z.B. eine Schleife, statt haufenweise Copy&Paste.
Stefan U. schrieb: > Überlass das mal dem Compiler, der wird schon guten Code erzeugen. > > Für a=b/2 und a=b<<1 erzeugt der Compiler exakt den selben Bytecode. Erstens hast Du da was verdreht (du meintest Rechtsschift) und zweitens hab ich schon Compiler gesehen (vorzugsweise solche die mit K anfangen und mit l aufhören und viel Geld verlangen) die sind nicht imstande eine Division durch eine 2^n Konstante als solche zu erkennen und ziehen dann die komplette Integer-Division mit rein. Allerdings muss man sich beim Rechtsschift vorher schlau machen was auf dieser Platform und bei diesem Compiler mit negativen Zahlen passiert.
> Allerdings muss man sich ... schlau machen was auf ...mit negativen > Zahlen passiert. Es geht um unsigned Integer, die können nicht negativ werden. Aber der Hinweis ist sinnvoll, bei signed Integer muss man aufpassen. > hab ich schon Compiler gesehen ... die sind nicht imstande (sind) eine > Division durch eine 2^n Konstante als solche zu erkennen und ziehen > dann die komplette Integer-Division mit rein. Na das sind ja erstaunlich dumme Compiler. Nicht mehr sonderlich Zeitgemäß, würde ich mal sagen.
https://www.mikrocontroller.net/articles/AVR_Arithmetik#Bin.C3.A4r_zu_BCD_-_Umwandlung Die Addition von 0x33 ist trickreich Die Formulierung am Anfang sollte sicher "HEX zu ASCII" heissen, nicht "DEC zu ASCII". Das wäre ja zu einfach.
Stefan U. schrieb: >>> bessere Alternative als ein x/10? >> x * 0,1 > > Ach komm schon, jetzt wird's aber schäbig. Das "schäbig" mußt Du mir schon begründen. Wenn es nur Integer-Zahlen sein sollen geht es auch mit Subtraktionen: Beitrag "Re: ADC-Wert auf 4 7-Segmentanzeigen darstellen ohne viel Rechenleistung" Wieviel Byte da nun gebraucht werden, habe ich nicht gezählt.
m.n. schrieb: > Draco schrieb: >> bessere Alternative als ein x/10? > > x * 0,1 Das mach mal vor. Den Unterschied zwischen 1/10 und 1.0/10.0 kennst du?
W.A. schrieb: > Das mach mal vor. Den Unterschied zwischen 1/10 und 1.0/10.0 kennst du? Für x = 123.456 x / 10 = 12.3456 x * 0.1 = 12.3456 Bitteschön ;-)
Double Dabble schrieb: > Double Dabble ist recht einfach. Und es ist genau das: Draco schrieb: > Vielleicht ein bitshift mit aufaddieren? https://en.wikipedia.org/wiki/Double_dabble Gruß Jobst
>>>> bessere Alternative als ein x/10? >>> x * 0,1 >> Ach komm schon, jetzt wird's aber schäbig. > Das "schäbig" mußt Du mir schon begründen. x*0,1 ist genau das Gleiche wie x/10. Nicht nur Mathematisch, sondern auch für den Mikrocontroller. Der Compiler erzeugt in beiden Fällen des selben Code. Der Vorschlag ist daher vollkommen sinnlos.
Stefan U. schrieb: > Der Compiler erzeugt in beiden Fällen des > selben Code. Der Compiler verwendet dafür aber aufgrund fehlenden Wissens eine Softdivisionsfunktion mit entsprechend langer Laufzeit. Genau das will der TO ja nicht. Hier mal ein Beispiel für einen vorzeichenbehafteten 16-bit-Wert in ASCII, führende Nullen werden weggelassen, ein Dezimalpunkt wird eingefügt. Es ist also ein Festkommawert mit einer Nachkommastelle, deswegen nicht bei den Stellen verwirren lassen.
1 | /**** Ausgabe als -####.# oder ####.# an RS232 ****/
|
2 | |
3 | void write_snnnndn(int16_t value) { |
4 | uint8_t tchr, lz; |
5 | |
6 | lz = 1; |
7 | if (value < 0) { // wenn negativ, invertieren und Minus |
8 | value = -value; |
9 | tchr = '-'; |
10 | serial_write(tchr); |
11 | }
|
12 | tchr = '0'; |
13 | while (value >= 10000) { // Tausender |
14 | value -= 10000; |
15 | tchr++; |
16 | }
|
17 | if (!(tchr=='0')) { |
18 | serial_write(tchr); |
19 | lz = 0; |
20 | }
|
21 | tchr = '0'; |
22 | while (value >= 1000) { // Hunderter |
23 | value -= 1000; |
24 | tchr++; |
25 | }
|
26 | if (!(tchr=='0') || !lz) { |
27 | serial_write(tchr); |
28 | lz = 0; |
29 | }
|
30 | tchr = '0'; |
31 | while (value >= 100) { // Zehner |
32 | value -= 100; |
33 | tchr++; |
34 | }
|
35 | if (!(tchr=='0') || !lz) { |
36 | serial_write(tchr); |
37 | }
|
38 | tchr = '0'; |
39 | while (value >= 10) { // Einer |
40 | value -= 10; |
41 | tchr++; |
42 | }
|
43 | serial_write(tchr); |
44 | serial_write('.'); |
45 | tchr = '0'; |
46 | while (value >= 1) { // Nachkomma |
47 | value -= 1; |
48 | tchr++; |
49 | }
|
50 | serial_write(tchr); |
51 | }
|
In ASM geht das noch etwas knapper, aber das würde die Koniferen hier im Forum verwirren.
> Und es ist genau das: > Draco schrieb: >> Vielleicht ein bitshift mit aufaddieren? Dann soll er das gefälligst auch hinschreiben.
Double Dabble schrieb: > Dann soll er das gefälligst auch hinschreiben. ?? Steht im Eröffnungspost. Gruß JObst
Stefan U. schrieb: > x*0,1 ist genau das Gleiche wie x/10. Nicht nur Mathematisch, sondern > auch für den Mikrocontroller. Der Compiler erzeugt in beiden Fällen des > selben Code. Eben nicht! Selbst bei einem AVR8 besteht ein Unterschied zwischen Multiplikation, die mit dem MUL-Befehl verkürzt und beschleunigt werden kann, und der Division, die deutlich aufwendiger mit Subtraktionen und Bitschieberei erledigt werden muß. Bei der Fragestellung x/10 gehe ich bei x zunächst einmal von einer reellen Zahl aus, weshalb ein Vorschlag von itoa() für x ein Integer impliziert. Das geht aber aus der Eingangsfrage nicht eindeutig hervor.
m.n. schrieb: > Bei der Fragestellung x/10 gehe ich bei x zunächst einmal von einer > reellen Zahl aus Ich gehe in Verbindung mit uC immer zuallererst von dessen generischem Format, also einer Ganzzahl in binärer Darstellung (aka Integer) aus. Demnach berechnet der die Division mit Erebnis und Rest, und eben nicht mit einer rellen Zahl als Resultat.
:
Bearbeitet durch Moderator
Lothar M. schrieb: > m.n. schrieb: >> Bei der Fragestellung x/10 gehe ich bei x zunächst einmal von einer >> reellen Zahl aus > Ich gehe in Verbindung mit uC immer zuallererst von dessen generischem > Format, also einer Ganzzahl in binärer Darstellung (aka Integer) aus. Ja gut, dann bezeichne ich diesen Typen aber mit i, j, k, ... und nicht mit x, y, z.
m.n. schrieb: > Ja gut, dann bezeichne ich diesen Typen aber mit i, j, k, ... > und nicht mit x, y, z. Was ist da der Unterschied? Man kann auch a, b und c nehmen...
>> x*0,1 ist genau das Gleiche wie x/10 > Eben nicht! Selbst bei einem AVR8 besteht ein Unterschied > zwischen Multiplikation, die mit dem MUL-Befehl verkürzt und > beschleunigt werden kann, und der Division, die deutlich > aufwendiger mit Subtraktionen und Bitschieberei erledigt werden muß. Das wollte ich überprüfen und kam zu einem überraschenden Ergebnis:
1 | __attribute__ ((noinline)) uint16_t durch_zehn(uint16_t a) |
2 | {
|
3 | return a/10; |
4 | }
|
5 | |
6 | __attribute__ ((noinline)) uint16_t mal_null_komma_eins(uint16_t a) |
7 | {
|
8 | return a*0.1; |
9 | }
|
Compiliert mit -O0 erzeugt die Multiplikation deutlich umfangreicheren Code:
1 | 00000080 <durch_zehn>: |
2 | 80: cf 93 push r28 |
3 | 82: df 93 push r29 |
4 | 84: 00 d0 rcall .+0 ; 0x86 <durch_zehn+0x6> |
5 | 86: cd b7 in r28, 0x3d ; 61 |
6 | 88: de b7 in r29, 0x3e ; 62 |
7 | 8a: 9a 83 std Y+2, r25 ; 0x02 |
8 | 8c: 89 83 std Y+1, r24 ; 0x01 |
9 | 8e: 89 81 ldd r24, Y+1 ; 0x01 |
10 | 90: 9a 81 ldd r25, Y+2 ; 0x02 |
11 | 92: 9c 01 movw r18, r24 |
12 | 94: ad ec ldi r26, 0xCD ; 205 |
13 | 96: bc ec ldi r27, 0xCC ; 204 |
14 | 98: 0e 94 a5 00 call 0x14a ; 0x14a <__umulhisi3> |
15 | 9c: 96 95 lsr r25 |
16 | 9e: 87 95 ror r24 |
17 | a0: 96 95 lsr r25 |
18 | a2: 87 95 ror r24 |
19 | a4: 96 95 lsr r25 |
20 | a6: 87 95 ror r24 |
21 | a8: 0f 90 pop r0 |
22 | aa: 0f 90 pop r0 |
23 | ac: df 91 pop r29 |
24 | ae: cf 91 pop r28 |
25 | b0: 08 95 ret |
26 | |
27 | 000000b2 <mal_null_komma_eins>: |
28 | b2: cf 93 push r28 |
29 | b4: df 93 push r29 |
30 | b6: 00 d0 rcall .+0 ; 0xb8 <mal_null_komma_eins+0x6> |
31 | b8: cd b7 in r28, 0x3d ; 61 |
32 | ba: de b7 in r29, 0x3e ; 62 |
33 | bc: 9a 83 std Y+2, r25 ; 0x02 |
34 | be: 89 83 std Y+1, r24 ; 0x01 |
35 | c0: 89 81 ldd r24, Y+1 ; 0x01 |
36 | c2: 9a 81 ldd r25, Y+2 ; 0x02 |
37 | c4: cc 01 movw r24, r24 |
38 | c6: a0 e0 ldi r26, 0x00 ; 0 |
39 | c8: b0 e0 ldi r27, 0x00 ; 0 |
40 | ca: bc 01 movw r22, r24 |
41 | cc: cd 01 movw r24, r26 |
42 | ce: 0e 94 e3 00 call 0x1c6 ; 0x1c6 <__floatunsisf> |
43 | d2: dc 01 movw r26, r24 |
44 | d4: cb 01 movw r24, r22 |
45 | d6: 2d ec ldi r18, 0xCD ; 205 |
46 | d8: 3c ec ldi r19, 0xCC ; 204 |
47 | da: 4c ec ldi r20, 0xCC ; 204 |
48 | dc: 5d e3 ldi r21, 0x3D ; 61 |
49 | de: bc 01 movw r22, r24 |
50 | e0: cd 01 movw r24, r26 |
51 | e2: 0e 94 49 01 call 0x292 ; 0x292 <__mulsf3> |
52 | e6: dc 01 movw r26, r24 |
53 | e8: cb 01 movw r24, r22 |
54 | ea: bc 01 movw r22, r24 |
55 | ec: cd 01 movw r24, r26 |
56 | ee: 0e 94 b4 00 call 0x168 ; 0x168 <__fixunssfsi> |
57 | f2: dc 01 movw r26, r24 |
58 | f4: cb 01 movw r24, r22 |
59 | f6: 0f 90 pop r0 |
60 | f8: 0f 90 pop r0 |
61 | fa: df 91 pop r29 |
62 | fc: cf 91 pop r28 |
63 | fe: 08 95 ret |
Compiliert mit -O1 erzeugt die Multiplikation immer noch deutlich umfangreicheren Code:
1 | 00000080 <durch_zehn>: |
2 | 80: 9c 01 movw r18, r24 |
3 | 82: ad ec ldi r26, 0xCD ; 205 |
4 | 84: bc ec ldi r27, 0xCC ; 204 |
5 | 86: 0e 94 70 00 call 0xe0 ; 0xe0 <__umulhisi3> |
6 | 8a: 96 95 lsr r25 |
7 | 8c: 87 95 ror r24 |
8 | 8e: 96 95 lsr r25 |
9 | 90: 87 95 ror r24 |
10 | 92: 96 95 lsr r25 |
11 | 94: 87 95 ror r24 |
12 | 96: 08 95 ret |
13 | |
14 | 00000098 <mal_null_komma_eins>: |
15 | 98: bc 01 movw r22, r24 |
16 | 9a: 80 e0 ldi r24, 0x00 ; 0 |
17 | 9c: 90 e0 ldi r25, 0x00 ; 0 |
18 | 9e: 0e 94 ae 00 call 0x15c ; 0x15c <__floatunsisf> |
19 | a2: 2d ec ldi r18, 0xCD ; 205 |
20 | a4: 3c ec ldi r19, 0xCC ; 204 |
21 | a6: 4c ec ldi r20, 0xCC ; 204 |
22 | a8: 5d e3 ldi r21, 0x3D ; 61 |
23 | aa: 0e 94 14 01 call 0x228 ; 0x228 <__mulsf3> |
24 | ae: 0e 94 7f 00 call 0xfe ; 0xfe <__fixunssfsi> |
25 | b2: cb 01 movw r24, r22 |
26 | b4: 08 95 ret |
Offensichtlich ist hier in beiden Fällen entscheidend, dass bei der Multiplikation eine Fließkommazahl verwendet wurde, bei der Division jedoch nur Integer vorkommen. Da habe ich mal wieder etwas dazu gelernt.
Rene K. schrieb: > Peter D. schrieb: >> Ich habs grad mal compiliert, itoa() benötigt wahnsinnig enorme, extrem >> riesige, unvorstellbar gigantische 134 Byte Flash, das sind volle 0,4% >> Deines ATmega32. >> Findest Du es nicht selber lachhaft, darum so ein Getöse zu machen? > > Das Argument lasse ich gelten :-D Leider muss ich viele Werte umwandeln > um sie via UART rauszuschicken. In der Summierung macht sich das schon > bezahlbar ja. Und "Getöse" war es ja nichtmal, ging ja alles flott von > der Hand und hat mich ja auch zeitlich nicht sonderlich aufgehalten. > > Das andere wäre ja auch noch eine Spur einfacher gewesen, ich könnte ja > auch die Daten direkt rausschicken und auf dem Host die Umrechnung > machen lassen. Aber so isses schöner, passt, is kleiner und läuft. Naja. Ich täte zunächst mal kucken, wo denn die ganze Rechenzeit hinfließt. Dazu gibts zum Beispiel die Möglichkeit, mittels Timer zu zählen, wieviele Taktzyklen denn eine bestimmte Funktion wirklich benötigt. Oder man setzt sich einen Pin, das Scope kann dir DIREKT sagen, wie langer der Controller da drin rumgammelt. Gemittelt Minuten wenn es sein muss. Nur Funktionen, in denen er lange ist, sollte man überhaupt anschauen. Dabei stellt sich oft heraus, dass eine als wichtig betrachtete Optimierung nur im unteren Promillebereich effektiv ist, aber eine andere Funtkion wegen eines simplen Fehlers massig Zyklen frisst. Ein strategisch ungeschickt verwendetes % mir schon mal 20% CPU-Last gekostet. Vor allem stellt sich IMMER heraus, dass das anders ist, als man geglaubt hat :-) Premature optimization is the root of all evil. (Donald Knuth)
x*102/1024 Multiplikation und shift sollten recht schnell gehen. Sollte, wenn ich mich nicht täusche keinen Rundungsfehler haben.
Lothar M. schrieb: > Was ist da der Unterschied? Man kann auch a, b und c nehmen... Der Unterschied besteht im Programmierstil, der sich in Anlehnung an K&R ergeben hat. Eine Character-Variable bekommt 'c', ein String 's', Pointer vorzugsweise 'p' und 'q' und eine temporäre Variable 'temp' als Bezeichner. Angelehnt an mathematische Funktionen stellen 'x' und 'y' reelle Zahlen dar. Einfaches Beispiel: y = ax + b Das sollte doch nachvollziehbar sein?
Stefan U. schrieb: > Offensichtlich ist hier in beiden Fällen entscheidend, dass bei der > Multiplikation eine Fließkommazahl verwendet wurde, bei der Division > jedoch nur Integer vorkommen. > > Da habe ich mal wieder etwas dazu gelernt. Vergleich doch auch noch die Bereechnungen für a *= 10; und a /= 0.1; ;-)
Stefan U. schrieb: > Compiliert mit -O1 erzeugt die Multiplikation immer noch deutlich > umfangreicheren Code: > 00000080 <durch_zehn>: > 80: 9c 01 movw r18, r24 > 82: ad ec ldi r26, 0xCD ; 205 > 84: bc ec ldi r27, 0xCC ; 204 > 86: 0e 94 70 00 call 0xe0 ; 0xe0 <__umulhisi3> ... Siehe da, bei der Division mit konstanter 10 wird auch multipliziert. Die Verwendung von float bei Multiplikation mit 0.1 macht es natürlich aufwendiger. Würde mich wundern, wenn das bei itoa nicht auch so gemacht würde.
:
Bearbeitet durch User
m.n. schrieb: > Angelehnt an mathematische Funktionen stellen 'x' und 'y' reelle Zahlen > dar. Einfaches Beispiel: y = ax + b Das steht ja jedem frei, es so zu halten, besonders, wenn er z.B. als Prof Mathe- oder Informatik-Vorlesungen hält. Wenn ich aber in einer Mikrocontroller-Anwendung z.B. in einer Integer-Variablen den Verfahrweg in x-Richtung speichern will, benenne ich die Variable deshalb nicht in "i" um, da ist mir Mathe und K&R Schnurz... Und vom Namen auf den Typ zu schließen ist sicher auch nicht im Sinne von K&R konsequent. Daß x/10 anderen Code erzeugt, als x * 0.1 (mit x als int), ist doch klar definiert: "0.1" ist eine reelle Zahl, wird also als float behandelt, daher wird der 2. Operand immer intern vor der Operation auch in float umgewandelt. "10" ist dagegen eine ganze Zahl, folglich wird "x/10" als int/int = Interger-Division verarbeitet. Achim schrieb: > Keine Rundungsfehler bis x==1024 auch darunter schon: 900 * 102 / 1024 = 89
:
Bearbeitet durch User
m.n. schrieb: > Der Unterschied besteht im Programmierstil, der sich in Anlehnung an K&R > ergeben hat. Eine Character-Variable bekommt 'c', ein String 's', > Pointer vorzugsweise 'p' und 'q' und eine temporäre Variable 'temp' als > Bezeichner. > Angelehnt an mathematische Funktionen stellen 'x' und 'y' reelle Zahlen > dar. Einfaches Beispiel: y = ax + b Den Typ einer Variablen im Namen zu spiegeln hatte zu Zeiten von K&R noch Sinn. Heutzutage liefert so ziemlich jede IDE den Typ einer Variablen, sobald der Mauszeiger über ihr steht. Ein sinnvoller Variablenname soll repräsentieren, WAS in einer Variable steht. Ggf. kann dazu die Angabe der Einheit des gespeicherten Werts dazukommen. Viele Grüße, Stefan
Thomas E. schrieb: > Wenn ich aber in einer > Mikrocontroller-Anwendung z.B. in einer Integer-Variablen den Verfahrweg > in x-Richtung speichern will, benenne ich die Variable deshalb nicht in > "a" um, da ist mir Mathe und K&R Schnurz... Mir ist es Schnurz, ob Du Deinen Anfängerfehler bezüglich eines Verfahrweges beibehalten willst oder nicht. Bei mir werden diese Wege als 'float' bzw. 'double' in 'mm' verarbeitet. Da ist es dann egal, ob ein Geber 10 µm, 2,5 µm, 0,2 µm oder 0,1µm Auflösung hat. Man kann die Werte direkt miteinander verrechnen und bei Bedarf ohne Verlust von Genauigkeit/Auflösung auch nach 'inch' wandeln. Stefan K. schrieb: > Den Typ einer Variablen im Namen zu spiegeln hatte zu Zeiten von K&R > noch Sinn. Heutzutage liefert so ziemlich jede IDE den Typ einer > Variablen, sobald der Mauszeiger über ihr steht. Dann gehe ich mal davon aus, daß Du die K&R Zeiten nicht miterlebt hast, und immer eine IDE mit Mauszeiger in der Tasche durch die Gegend schleppst.
Thomas E. schrieb: > m.n. schrieb: >> Angelehnt an mathematische Funktionen stellen 'x' und 'y' reelle Zahlen >> dar. Einfaches Beispiel: y = ax + b > > Das steht ja jedem frei, es so zu halten, besonders, wenn er z.B. als > Prof Mathe- oder Informatik-Vorlesungen hält. Im Kontext der Fragestellung "Ich will eine Zahl in die String- Darstellung umwandeln und muß dafür ja immer wieder durch 10 dividieren" kommt man genau gar nicht auf die Idee, bei dieser Zahl könnte es sich um einen Fließkommatyp handeln. Das war eine reine Schutzbehauptung von Mino, um seine dämliche Aussage "x/10 ist genau das gleiche wie x*0.1" zu retten. Das Verfahren, suzessive durch 10 zu dividieren und die Divisions- Reste als Dezimalstellen der Zahl zu verwenden, funktioniert genau nur für Integer-Zahlen. Bei Fließkomma gibt es ja keinen Rest. Stefan K. schrieb: > Den Typ einer Variablen im Namen zu spiegeln hatte zu Zeiten > von K&R noch Sinn. IMNSHO hatte diese sogenannte "ungarische Notation" noch nie irgendeinen Vorteil. Na gut, man konnte die Größe des Quellcodes etwas aufblähen und so Produktivität vortäuschen ...
Achim schrieb: > Sollte, wenn ich mich nicht täusche keinen Rundungsfehler haben. Der Fehler dieser Rechnung ist etwa -0,4%. Und dieser Fehler wird sich natürlich über die anzuzeigenden Ziffern auswirken. Wenn z.B. 1000 angezeigt werden sollen, dann kommt beim stellenweisen Dividieren mit Integern(!) tatsächlich ein Wert von 990 auf dem Display heraus:
1 | 1000 -> Einer = 0 |
2 | 1000*102/1024 -> 99 -> Einer = 9 |
3 | 99 *102/1024 -> 9 -> Einer = 9 |
m.n. schrieb: > Lothar M. schrieb: >> Was ist da der Unterschied? Man kann auch a, b und c nehmen... > Der Unterschied besteht im Programmierstil, der sich in Anlehnung an K&R > ergeben hat. .... > Das sollte doch nachvollziehbar sein? Jetzt nach dieser zweiseitigen Erklärung schon. Allein: das ist mir zu kompliziert und ich gebe deshalb den Variablen einfach solche Namen, die auch ohne Insiderwissen selbsterklärend sind. Und ich muss heute nicht mehr an jede Zeichenkette ein s oder ein c oder ein ul anhängen, weil sich nämlich die Editoren so weiterentwickelt haben, dass sie mir diese "Merkarbeit" abnehmen. Der Editor zeigt mir den Datentyp, ich muss ihn nicht mehr in den Variablennamen packen... > Angelehnt an mathematische Funktionen stellen 'x' und 'y' reelle Zahlen > dar. Einfaches Beispiel: y = ax + b Genau diese "Grundschulrechnerei" mit x und y hat uns unser Professor seinerzeit am Anfang des ersten Semesters ganz bewusst ausgetrieben. Und wenn ich mich nicht irre tauchen auch in einer der Mitternachtsformeln p und q auf...
:
Bearbeitet durch Moderator
m.n. schrieb: > Dann gehe ich mal davon aus, daß Du die K&R Zeiten nicht miterlebt hast, > und immer eine IDE mit Mauszeiger in der Tasche durch die Gegend > schleppst. Ganz im Gegenteil, ich schätze meinen K&R (eine der ersten Ausgaben) sehr. Und ebenso schätze ich die Vorzüge moderner Entwicklungsumgebungen. Für die Variablen-Namensgebung ist zuallererst der Projekt Styleguide relevant. Die von Dir genannte Namensgebung habe ich bisher aber noch in keinem beschrieben gesehen. Gruß, Stefan
Axel S. schrieb: > Das Verfahren, suzessive durch 10 zu dividieren und die Divisions- > Reste als Dezimalstellen der Zahl zu verwenden, funktioniert genau > nur für Integer-Zahlen. Das Verfahren - welches ich selbst bei gewissen Aufgabenstellungen gern anwende - hat einen gravierenden Nachteil: Du bekommst die Ziffern in der Reihenfolge Einer-Zehner-Hunderter-Tausender. Für eine Ausgabe per RS232 oder Display mußt Du sie also zwischenspeichern und in umgekehrter Reihenfolge ausgeben. Deswegen verwende ich in diesen Fällen gern obige Routine. Da muß die While-Schleife für jede Dezimalstelle maximal Neunmal durchlaufen werden. Bei einer Softdivision mit Integer läuft jede Stelle schon 16mal (Shift durch 16 Bits) durch, zuzüglich Aufrufs der Divisionsroutine und Variablenübergabe.
Die Konvention, daß alle Variablen, die mit I,J,K,L,M oder N beginnen, Interger sind, und alle anderen REAL (float), stammt ursprünglich von FORTRAN. Wenn man sich in FORTRAN daran hält, kann man die Typvereinbarung weglassen, und spart ein paar Lochkarten.
Leo C. schrieb: > Die Konvention, daß alle Variablen, die mit I,J,K,L,M oder N beginnen, > Interger sind, und alle anderen REAL (float), stammt ursprünglich von > FORTRAN. Ich nehme grundsätzlich nur 1- und 2-stellige Variablennamen in Großbuchstaben, weil Basic das so wollte. Und Zeilennummern! Ganz wichtig sind Zeilennummern!
Axel S. schrieb: > Das war eine reine Schutzbehauptung von Mino, um seine dämliche > Aussage "x/10 ist genau das gleiche wie x*0.1" zu retten. Na, schlecht geschlafen letzte Nacht? > Das Verfahren, suzessive durch 10 zu dividieren und die Divisions- > Reste als Dezimalstellen der Zahl zu verwenden, funktioniert genau > nur für Integer-Zahlen. Bei Fließkomma gibt es ja keinen Rest. Wenn man den ganzzahligen Anteil subtrahiert, kann man auch mit dem Rest weiterarbeiten. Aber ich sehe schon, Vielfalft ist out - Monotonie in. Leo C. schrieb: > Die Konvention, daß alle Variablen, die mit I,J,K,L,M oder N beginnen, > Interger sind, und alle anderen REAL (float), stammt ursprünglich von > FORTRAN. Oh, vor über 40 Jahren habe ich FORTRAN gemacht; kann sein, daß ich es damals verinnerlicht hatte. C war ja erst deutlich später angesagt.
Timm T. schrieb: > Axel S. schrieb: >> Das Verfahren, suzessive durch 10 zu dividieren und die Divisions- >> Reste als Dezimalstellen der Zahl zu verwenden, funktioniert genau >> nur für Integer-Zahlen. > > Das Verfahren - welches ich selbst bei gewissen Aufgabenstellungen gern > anwende - hat einen gravierenden Nachteil: Du bekommst die Ziffern in > der Reihenfolge Einer-Zehner-Hunderter-Tausender. Für eine Ausgabe per > RS232 oder Display mußt Du sie also zwischenspeichern und in umgekehrter > Reihenfolge ausgeben. Ganz recht. Andererseits ist dieser Nachteil vergleichsweise simpel zu kompensieren: man schreibt die Dezimalzahlen einfach beginnend vom Ende rückwärts in den Buffer (den man für einen String ja ohnehin braucht). Schlimmstenfalls muß man den String danach noch einmal kopieren. Der Overhead dafür ist vergleichsweise gering - die Division dominiert die Laufzeit der Schleife bei weitem. Außer vielleicht bei Architekturen die nativ dividieren können. Der Vorteil des Verfahrens ist, daß man es jedem Anfänger leicht erklären kann. Das Gegenbeispiel wäre "Schieben mit BCD-Korrektur" aka "double dabble" [1] aka "Dreierkorrektur" [2]. Die Tatsache, daß es jetzt nur noch eine Division mit einer Konstante gibt, macht dem Compiler das Leben (die Optimierung) leichter. [1] was ist das für ein bescheuerter Name? [2] unter diesem Namen steht es in diversen Fachbüchern hier
Axel S. schrieb: > den man für einen String ja ohnehin braucht Nope, für die Ausgabe an RS232 oder Display brauchst Du eben gerade keinen extra String, wie obige Routine zeigt. Du schmeißt die Zeichen einfach einzeln raus. Und bei RS232 wartest Du eh die meiste Zeit aufs Senden, wenn Du nicht mit Sendebuffer* und Interrupt arbeitest. *) Wobei der Sendebuffer hier wieder was anderes als der Zeichenkettenstring ist.
> Das war eine reine Schutzbehauptung von Mino, um seine dämliche > Aussage "x/10 ist genau das gleiche wie x*0.1" zu retten. Nein, du hast da eine Aussage von MIR (nicht Mino) in den falschen Kontext gestellt. Ich habe behauptet "x/10 ist genau das gleiche wie x*0.1", nachdem jemand die Multiplikation als bessere Alternative zur Division empfohlen hat. Nun habe ich allerdings selbst herausgefunden und hier gezeigt, dass diese beiden Schreibweisen doch nicht den selben Code ergeben und dass die Multiplikation in diesem Fall sogar noch schlechter ist, als die Division.
Hallo, wenn ich eine Ganzzahl nach ASCII konvertieren will kann ich, wie bereits diskutiert: 1.: sukzessive durch 10 dividieren und den Rest als ASCII Zeichen speichern 2.: 10er Stellen sukzessive durch Subtraktion ermitteln und nach ASCII wandeln, hier muss ich mit der grössten Stelle anfangen Auf den ersten Blick ist die erste Methode elegant und kompakt. Ohne Divisionsbefehl, der das Ergebnis mit Rest liefert, wird das ganze jedoch kompliziert. Man braucht eine Näherung für x/10, die für alle Werte von x genau ist und man muss den Rest auch noch bilden. der Ansatz: x/10 = x*26/256 ist genau bis x=68 x/10 = (x*25+x/2+x/8)/256 ist genau bis x=1048 ... Und den Rest muss man bilden mit r=x-div(x,10)*10. Ich denke also die Methode sukzessive zu Subtrahieren ist besser als man denkt. Waldo
Waldo schrieb: > Ich denke also die Methode sukzessive zu Subtrahieren ist besser als man > denkt. Ja, ich dachte auch erst: Das ist ja billig. Aber es ist sehr flexibel auf verschiedene Stellenzahlen, Nachkommastellen, mit und ohne Leading Zero usw. anpaßbar. Double Dabble gefällt mir, aber ist schwerer durchschaubar und damit fehleranfällig, wenn man es anpassen muß. Und es benötigt die Zwischenspeicherstellen, nachträgliches Extrahieren aus den Nibbles und Wandeln von BCD zu ASCII.
zu x*102/1024: mit x=10 x*10/1024 = 1020/1024 = 0 liefert 126 Fehler bis x=255 Waldo
Timm T. schrieb: >> den man für einen String ja ohnehin braucht > > Nope, für die Ausgabe an RS232 oder Display brauchst Du eben gerade > keinen extra String, wie obige Routine zeigt. Du schmeißt die Zeichen > einfach einzeln raus. Ich halte es für extrem schlechten Stil, Konvertierung und Ausgabe derart "verzahnt" zu implementieren. IMNSHO gehört die Konvertierung in eine extra Funktion (itoa läßt grüßen). Und dann muß der Rückgabewert irgendwo zwischengespeichert werden. Waldo schrieb: > Ich denke also die Methode sukzessive zu Subtrahieren ist besser > als man denkt. Sie skaliert halt nicht so gut für große Zahlen. Überhaupt muß man den möglichen Umfang des Wertebereichs vorher kennen; und wehe, wenn die übergebene Zahl größer ist als die Routine erwartet. Man sollte hier keine Abkürzung vornehmen und entweder den gesamten Wertebereich des Argumenttyps abdecken oder zumindest auf Überschreitung des Wertebereichs testen. Die Variante mit der Division paßt sich ganz von selber an den Werte- bereich an. Und sie ist trivial auf eine andere Zahlenbasis umzustellen. Ich glaube statt dessen, der Aufwand der Division/Modulus-Operation wird notorisch überschätzt.
> Ich halte es für extrem schlechten Stil, Konvertierung und > Ausgabe derart "verzahnt" zu implementieren. Andere Leute halten es für schlechten Stil, bei Mikrocontrollern Speicher zu vergeuden.
Axel S. schrieb: > Ich glaube statt dessen, der Aufwand der Division/Modulus-Operation wird > notorisch überschätzt. Eine echte Division mit 2 Unbekannten ist schon recht aufwendig. Aber eine Division durch eine Konstante wird von guten Compilern eh durch Multiplikation und Shiften ersetzt. Siehe oben.
Andere halten es für schlechten Stil, aufgrund von irgendwelchem rein gefühlten Optimierungsbedarf eine simple Division mit viel Arbeitszeit in die Unwartbarkeit zu "optimieren", ohne belegen zu können, dass sich das lohnt. Und alle diese Sichtweisen sind Religionen, deshalb bringt jede Diskussion rein gar nichts, außer dass alle Teilnehmer sich hinterher gegenseitig für Idioten halten.
Axel S. schrieb: > Ich halte es für extrem schlechten Stil, Konvertierung und Ausgabe > derart "verzahnt" zu implementieren. IMNSHO gehört die Konvertierung in > eine extra Funktion (itoa läßt grüßen). Und dann muß der Rückgabewert > irgendwo zwischengespeichert werden. Wem soll man es denn nun recht machen? Programmiert man sauber und alles schön getrennt (womöglich noch mit 'float'-Zahlen), wird man angemacht, den µC gandenlos zu überlasten. Programmiert man 'verzahnt', was für den Ablauf eines Programmes sehr bechleunigend werden kann, wird man angemacht, keine Struktur im Programm zu haben. Im vorliegenden Fall kann ich Timms Ansicht nur bekräftigen. Zum einen werden die Zeichen von MSD nach LSD gewandelt und können verzahnt per UART aber auch auf ein LCD ausgegeben werden. Gerade bei Letzterem erspart man sich die 40-50 µs Wartezeit/Zeichen bei der Ausgabe.
Stefan U. schrieb: >>> Für a=b/2 und a=b<<1 erzeugt der Compiler exakt den selben Bytecode. >> Bestimmt nicht. > > Sorry, die Pfeile sind natürlich falsch herum. Wenn b signed ist und der Compiler mit Zweierkomplement rechnet, darf er das nicht machen. Für z.B. b = -3 ergibt b/2 das erwartete -1 und bei b>>1 kommt -2 heraus.
> Wenn b signed ist
Es geht um unsigned Integer. Außerdem bist du nicht der erste, der
darauf hinweist.
Es wurde alles mehrfach gesagt, nur noch nicht von jedem.
Axel S. schrieb: > Ich halte es für extrem schlechten Stil, Konvertierung und Ausgabe > derart "verzahnt" zu implementieren. Ich halte es für extrem schlechten Stil, im µC ineffizient zu programmieren. Axel S. schrieb: > Sie skaliert halt nicht so gut für große Zahlen. Überhaupt muß man den > möglichen Umfang des Wertebereichs vorher kennen Wenn ich im µC mit Ganzzahlen / Festkommazahlen arbeite, muß ich IMMER darauf achten, in welchem Wertebereich ich mich bewege. Und was das Skalieren angeht: Eine Long-Division in Software skaliert besser als eine Subtraktion mit 4 Bytes? Wohl kaum.
Waldo schrieb: > Hallo, > wie oft wurde x/10 in diesem Forum schon diskutiert? Schon X-mal... MfG Paul
Draco schrieb: > Gibt es eine > bessere Alternative als ein x/10? Vielleicht ein bitshift mit > aufaddieren? Mal 1.6 und durch 16 Teilen?
Timm T. schrieb: > Axel S. schrieb: >> Ich halte es für extrem schlechten Stil, Konvertierung und Ausgabe >> derart "verzahnt" zu implementieren. > > Ich halte es für extrem schlechten Stil, im µC ineffizient zu > programmieren. Das ist noch nicht raus, ob das wirklich ineffizient ist. Ich habe letzthin z.B. viel µC Code geschrieben, wo die gleiche Ausgabe einmal lokal auf das LCD und einmal per UART an den PC geht. An dieser Stelle wäre es ineffizient, die Konvertierung zweimal zu machen. Es kommt immer auf das Umfeld an. Aber eine Konvertierungsfunktion a'la itoa() hat zumindest den Vorteil, universell zu sein. >> Sie skaliert halt nicht so gut für große Zahlen. Überhaupt muß man den >> möglichen Umfang des Wertebereichs vorher kennen > > ... was das Skalieren angeht: Eine Long-Division in Software skaliert > besser als eine Subtraktion mit 4 Bytes? Wohl kaum. Die Division muß man nur einmal pro Dezimalstelle machen. Die Subtraktion bis zu neunmal. Bei der Laufzeit wird sich das nicht viel nehmen, bei der Codegröße gewinnt mit Sicherheit die Division. Mit um so mehr Abstand, je größer die Zahlen werden. Aber falls das so rüber gekommen sein sollte: ich will auf keinen Fall die Methode des suzessiven Dividierens als silver bullet hinstellen. Allein schon deswegen, weil es so etwas gar nicht gibt. Was die beste Lösung ist, variiert mit der konkreten Aufgabe und der konkreten Hardware.
Markus W. schrieb: > Mal 1.6 und durch 16 Teilen? Ach was! Nägel mit Köpfen machen und durch NULL teilen. Dann sieht man wenigstens, ob der Kompiler fehlertolerant ist. SCNR Paul
Axel S. schrieb: > Die Division muß man nur einmal pro Dezimalstelle machen. Die > Subtraktion bis zu neunmal. Oha. Du weißt aber schon, was bei einer Software-Division passiert? Ich verrate nur so viel: Schleife und jedes Bit darf mal vor.
Timm T. schrieb: > Axel S. schrieb: >> Die Division muß man nur einmal pro Dezimalstelle machen. Die >> Subtraktion bis zu neunmal. > > Oha. Du weißt aber schon, was bei einer Software-Division passiert? > > Ich verrate nur so viel: Schleife und jedes Bit darf mal vor. Ahh. Es hat noch jemand gemerkt. Sehr schön.
Axel S. schrieb: > Ich halte es für extrem schlechten Stil, Konvertierung und Ausgabe > derart "verzahnt" zu implementieren. Warum sollte das schlecht sein? Wenn eh' nur zur Ausgabe diese Konvertierung nötig ist (der absolute Regelfall, denn wer rechnet schon mit Zahlen in dezimaler ASCII-Repräsentation!!!), dann wäre es sogar extrem dumm, Speicher und Rechenzeit zu verschwenden, nur um das zu formal zu trennen. Natürlich gibt es Anwendungsfälle, wo es trotzdem sinnvoll ist, das zu trennen, z.B. wenn eine tabellierte Ausgabe wünschenswert ist. Dann tut man es halt, aber eben nur dann, wenn sich daraus auch ein sachlicher Vorteil ergibt, und nicht nur, um irgendeiner mehr oder weniger idiotischen "Stil"-Vorgabe zu genügen... "Form follows function". Das ist die einzig akzeptable Stilvorgabe.
c-hater schrieb: > Natürlich gibt es Anwendungsfälle, wo es trotzdem sinnvoll ist, das zu > trennen, z.B. wenn eine tabellierte Ausgabe wünschenswert ist. Auch da und gerade da. Zum Bleistift habe ich bei meiner Heizungssteuerung alle Temperaturen als Festpunktzahl mit einer Dezimalstelle gespeichert. Für jede Temperatur, die auf dem Display dargestellt wird, wird dann die gleiche Routine aufgerufen, die die Gleitpunktzahl auseinandernimmt, in ASCII wandelt, führende Nullen durch Leerzeichen ersetzt (damit die Position auf dem Display stimmt) und ein °C dranhängt. Dieser Routine muß ich nur den Wert übergeben und bekomme das Ergebnis aufs Display. Wenn ich das erst über itoa, String usw. machen wollte, wäre das deutlich umständlicher.
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.