Forum: Mikrocontroller und Digitale Elektronik ItoA mit Uint64_t


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Benjamin S. (benjamin_s316)


Bewertung
0 lesenswert
nicht lesenswert
Moin Moin

Ich versuche die Wandlung einer 64Bit unsigned int in ASCII.

Folgende Funktion liefert leider falsche Werte

snr=0;
ItoA(snr,teststring);
draw_string_8x16_normal(10,210,teststring);

-> Ausgabe zeigt 125729


void ItoA( uint64_t z, char* Buffer )
{
    int i = 0;
    int j;
    char tmp;
    uint64_t u;    // In u bearbeiten wir den Absolutbetrag von z.

    // ist die Zahl negativ?
    // gleich mal ein - hinterlassen und die Zahl positiv machen
    // die einzelnen Stellen der Zahl berechnen
    do
        {
            Buffer[i++] = '0' + u % 10;
            u /= 10;
        }
    while( u > 0 );

    // den String in sich spiegeln
    for( j = 0; j < i / 2; ++j )
        {
            tmp = Buffer[j];
            Buffer[j] = Buffer[i-j-1];
            Buffer[i-j-1] = tmp;
        }
    Buffer[i] = '\0';
}

von Kaj (Gast)


Bewertung
1 lesenswert
nicht lesenswert
Benjamin S. schrieb:
> uint64_t u;

Benjamin S. schrieb:
> do
>         {
>             Buffer[i++] = '0' + u % 10;
>             u /= 10;
>         }
>     while( u > 0 );

u ist nicht initialisiert, damit erhälst du logischer weise schrott.

von Hannes J. (Firma: _⌨_) (pnuebergang)


Bewertung
0 lesenswert
nicht lesenswert
Von der großartig im Kommentar angekündigten Behandlung negativer Zahlen 
ist auch nichts zu sehen. Auf 
http://hauptschule-bochum.de/wordpress/?incsub_wiki=testwiki findet man 
vermutlich das Original der Funktion.

von Draco (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Hannes J. schrieb:
> Von der großartig im Kommentar angekündigten Behandlung negativer Zahlen
> ist auch nichts zu sehen.

Hätte mich auch gewundert wenn ein Unsinged Integer negativ Vorbehaftet 
ist. ;-)

von Kaj (Gast)


Bewertung
-5 lesenswert
nicht lesenswert
Wenn du den String so spiegelst
1
// den String in sich spiegeln
2
Buffer[i--] = '\0';
3
for( j = 0; j < i ; j++, i-- ) {
4
    Buffer[j] ^= Buffer[i];
5
    Buffer[i] ^= Buffer[j];
6
    Buffer[j] ^= Buffer[i];
7
}
sparst du dir die extra tmp variable. Das ist aber geschmackssache.

von Ralph S. (jjflash)


Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
... schließe mich den Vorrednern komplett an (die schlicht eben 
schneller waren als ich während ich den Code angesehen und modifiziert 
hab).

Irgendwie ... bin ich wohl definitiv zu langsam !

von Mikro 7. (mikro77)


Bewertung
0 lesenswert
nicht lesenswert
Ralph S. schrieb:
>...während ich den Code angesehen und modifiziert hab...

Fast korrekt. Hat noch mit (int64_t) 0x8000000000000000 Probleme.

Für den kleinsten (negativen) Integer gibt es kein positives Pendant.

von Rolf M. (rmagnus)


Bewertung
0 lesenswert
nicht lesenswert
Eigentlich war doch uint64_t gefragt und nicht int64_t.

von Frank M. (ukw) (Moderator) Benutzerseite


Bewertung
0 lesenswert
nicht lesenswert
Rolf M. schrieb:
> Eigentlich war doch uint64_t gefragt und nicht int64_t.

Eben. Daher sollte man die Funktion auch UtoA() nennen.

von Dr. Sommer (Gast)


Bewertung
-3 lesenswert
nicht lesenswert
Warum selber machen, wenn die Standard Library das schon kann... Ok, ist 
für kleine Mikrocontroller nicht geeignet:
1
#include <sstream>
2
#include <cstdint>
3
#include <string>
4
#include <iostream>
5
6
int main () {
7
  uint64_t i = 0x1122334455667788;
8
  std::stringstream ss;
9
  ss << i;
10
  std::string res = ss.str ();
11
  std::cout << res;
12
}

von Jens G. (jensg)


Bewertung
1 lesenswert
nicht lesenswert
Mikro 7. schrieb:
> Ralph S. schrieb:
>>...während ich den Code angesehen und modifiziert hab...
>
> Fast korrekt. Hat noch mit (int64_t) 0x8000000000000000 Probleme.
>
> Für den kleinsten (negativen) Integer gibt es kein positives Pendant.
Ich habe zum Thema vorhandene Software erweitert und angepasst -- 
funktioniert prima beim Arduino.

Quelle: https://github.com/amdn/itoa_ljust
von mir: 
https://github.com/JensGrabner/snc98_Slash-Number-Calculator/tree/master/Software/Arduino/libraries/itoa_ljust

von Wilhelm M. (wimalopaan)


Bewertung
0 lesenswert
nicht lesenswert
Dr. Sommer schrieb:
> Warum selber machen, wenn die Standard Library das schon kann... Ok, ist
> für kleine Mikrocontroller nicht geeignet:
>
>
1
#include <sstream>
2
> #include <cstdint>
3
> #include <string>
4
> #include <iostream>
5
> 
6
> int main () {
7
>   uint64_t i = 0x1122334455667788;
8
>   std::stringstream ss;
9
>   ss << i;
10
>   std::string res = ss.str ();
11
>   std::cout << res;
12
> }

(Noch) einfacher geht es mit std::to_string(i) ...

Aber C++ war ja nicht gefragt ;-)

von Wilhelm M. (wimalopaan)


Bewertung
-2 lesenswert
nicht lesenswert
Jens G. schrieb:
> Mikro 7. schrieb:
>> Ralph S. schrieb:
>>>...während ich den Code angesehen und modifiziert hab...
>>
>> Fast korrekt. Hat noch mit (int64_t) 0x8000000000000000 Probleme.
>>
>> Für den kleinsten (negativen) Integer gibt es kein positives Pendant.
> Ich habe zum Thema vorhandene Software erweitert und angepasst --
> funktioniert prima beim Arduino.
>
> Quelle: https://github.com/amdn/itoa_ljust
> von mir:
> 
https://github.com/JensGrabner/snc98_Slash-Number-Calculator/tree/master/Software/Arduino/libraries/itoa_ljust

Sorry, ich kann einfach nicht nachvollziehen, dass man das in C++ mit 
C-Arrays macht, deren (hoffentlich ausreichende) Größe einer Annahme 
unterliegt, die der Compiler ja nicht prüfen kann!

von Jens G. (jensg)


Bewertung
0 lesenswert
nicht lesenswert
Wilhelm M. schrieb:
> Jens G. schrieb:
> Sorry, ich kann einfach nicht nachvollziehen, dass man das in C++ mit
> C-Arrays macht, deren (hoffentlich ausreichende) Größe einer Annahme
> unterliegt, die der Compiler ja nicht prüfen kann!

Ich verstehe die Nachfrage nicht. Der Quelltext ist getestet und 
funktioniert. .. ist bei Arduino etwa doppelt so schnell wie itoa(); 
ltoa(); .. und geht mit allen Datentypen von 8_bit bis 64_bit, mit und 
ohne Vorzeichen.

Bei VisualStudio gibt es Warnungen, da mehrmals Typumwandlungen 
angenommen werden. Es ist alles mit Bedacht verwendet worden. Dieser 
Test: https://github.com/miloyip/itoa-benchmark lief fehlerfrei bei mir 
durch.

von Wilhelm M. (wimalopaan)


Bewertung
-1 lesenswert
nicht lesenswert
Jens G. schrieb:
> Wilhelm M. schrieb:
>> Jens G. schrieb:
>> Sorry, ich kann einfach nicht nachvollziehen, dass man das in C++ mit
>> C-Arrays macht, deren (hoffentlich ausreichende) Größe einer Annahme
>> unterliegt, die der Compiler ja nicht prüfen kann!
>
> Ich verstehe die Nachfrage nicht. Der Quelltext ist getestet und
> funktioniert. .. ist bei Arduino etwa doppelt so schnell wie itoa();
> ltoa(); .. und geht mit allen Datentypen von 8_bit bis 64_bit, mit und
> ohne Vorzeichen.

Der Compiler wirds compilieren:
1
uint64_t x = 42;
2
char buffer[2];
3
itoa(x, 42);

Aber was passiert?

von Jens G. (jensg)


Bewertung
1 lesenswert
nicht lesenswert
Wilhelm M. schrieb:
> Der Compiler wirds compilieren:
>
>
1
> uint64_t x = 42;
2
> char buffer[2];
3
> itoa(x, 42);
4
>
>
> Aber was passiert?
Das Programm wird fehlerhaft ausgeführt werden, da zur Laufzeit nicht 
überprüft wird, wo in den Buffer geschrieben wird. Der Programmierer 
steht in der Verantwortung den Buffer entsprechend der zu erwartenden 
Zahlen zu wählen.  .. bei Pascal war alles noch anders - da gab es eine 
Typprüfung. Heute darf der Programmierer selbst darauf achten was ER 
tut.

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Bewertung
-1 lesenswert
nicht lesenswert
Jens G. schrieb:
> Wilhelm M. schrieb:
>> Der Compiler wirds compilieren:
>>
>>
1
>> uint64_t x = 42;
2
>> char buffer[2];
3
>> itoa(x, 42);
4
>>
>>
>> Aber was passiert?
> Das Programm wird fehlerhaft ausgeführt werden, da zur Laufzeit nicht
> überprüft wird, wo in den Buffer geschrieben wird.

Genau. Und das kann man bei C++ ja ganz elegant verhindern! Sowas macht 
man meinetwegen in C, aber nicht in C++!

von Jens G. (jensg)


Bewertung
0 lesenswert
nicht lesenswert
Wilhelm M. schrieb:
> Genau. Und das kann man bei C++ ja ganz elegant verhindern! Sowas macht
> man meinetwegen in C, aber nicht in C++!
Na dann freue ich mich auf die Verbesserungen meines Quelltextes. In C++ 
bin ich noch Anfänger, lerne gerne auch von anderen Profis. Ich habe den 
vorliegenden Quelltext einfach etwas erweitert und von Fehlern 
(Portierbarkeit) beseitigt. Hoffenlich funktioniert .. nach der 
Veränderung .. der Quelltext noch beim Arduino.

von Wilhelm M. (wimalopaan)


Bewertung
0 lesenswert
nicht lesenswert
Jens G. schrieb:
> Wilhelm M. schrieb:
>> Genau. Und das kann man bei C++ ja ganz elegant verhindern! Sowas macht
>> man meinetwegen in C, aber nicht in C++!
> Na dann freue ich mich auf die Verbesserungen meines Quelltextes. In C++
> bin ich noch Anfänger, lerne gerne auch von anderen Profis.

Ok, habe ich mir gedacht.
Nun, dann schnapp Dir ein paar gute Bücher (Breymann, alle von Scott 
Meyers sowie von Herb Sutter und Josuttis, oder schau auf Youtube die 
Serie von Jason Turner) und los geht's. Aber auf dieser Stufe solltest 
Du nicht stehen bleiben, wenn Du wirklich C++ machen möchtest und nicht 
nur C-Code mit einem C++-Compiler übersetzen.

: Bearbeitet durch User
von Jens G. (jensg)


Bewertung
0 lesenswert
nicht lesenswert
Wilhelm M. schrieb:
> Aber auf dieser Stufe solltest Du nicht stehen bleiben,
> wenn Du wirklich C++ machen möchtest und nicht
> nur C-Code mit einem C++-Compiler übersetzen.

Ich denke Du meinst man sollte anstelle von "array of char" lieber den 
Typ String verwenden und bei 8-bit µC kann dann nur dies zur Anwendung 
kommen: http://www.etlcpp.com/string.html

Ist das gemeint -- lieber "String" anstelle von "array of char" ?

von Wolfgang (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Jens G. schrieb:
> Der Programmierer steht in der Verantwortung den Buffer entsprechend
> der zu erwartenden Zahlen zu wählen.
Bei integer Datentypen bekannter Größe ist die maximale Größe der 
möglichen Zahlen und damit auch die maximale Größe des erforderlichen 
Zeichen-Arrays doch wohl nicht ganz unberechenbar. Was erwartest du da 
für Unwägbarkeiten, die der Programmierer entscheiden muss?

von Wilhelm M. (wimalopaan)


Bewertung
0 lesenswert
nicht lesenswert
Jens G. schrieb:
> Wilhelm M. schrieb:
>> Aber auf dieser Stufe solltest Du nicht stehen bleiben,
>> wenn Du wirklich C++ machen möchtest und nicht
>> nur C-Code mit einem C++-Compiler übersetzen.
>
> Ich denke Du meinst man sollte anstelle von "array of char" lieber den
> Typ String verwenden und bei 8-bit µC kann dann nur dies zur Anwendung
> kommen: http://www.etlcpp.com/string.html

Nicht notwendigerweise. Der wichtige Punkt ist: wird die Schnittstelle 
von
itoa() falsch benutzt, d.h. hier: verwendet der Anwender einen zu 
kleinen
Buffer / Container als Output-Parameter für den zu wandelnden 
Ganzzahltyp, dann darf das nicht kompilieren(!).

Da kann man sicher etl::string<> nehmen, aber auch std::array<> (oder 
auch char[N]) oder stringview<> oder eigene Container Realisierungen. 
Daran erkennt man, dass der Typ des Containers eigentlich egal ist unter 
der Voraussetzung, das der Elementtyp und die Größe passt. Also ein 
generischer Algorithmus.

Das führt dazu, dass man itoa() als template schreiben kann. Das 
template
leitet die Größe des Containers ab und compiliert nicht, wenn die zu 
klein / nicht passend ist im Vergleich zur Ganzzahl.

> Ist das gemeint -- lieber "String" anstelle von "array of char" ?

s.o. ... nein.

von Rolf M. (rmagnus)


Bewertung
0 lesenswert
nicht lesenswert
Wilhelm M. schrieb:
> Nicht notwendigerweise. Der wichtige Punkt ist: wird die Schnittstelle
> von itoa() falsch benutzt, d.h. hier: verwendet der Anwender einen zu
> kleinen Buffer / Container als Output-Parameter für den zu wandelnden
> Ganzzahltyp, dann darf das nicht kompilieren(!).

Nicht "zu klein für den Typ", sondern "zu klein für den Wertebereich". 
Wenn  ich einen Wert im Bereich 0 bis 80000 in einen String wandeln 
will, brauche ich nicht einen 12 Bytes großen Puffer dafür, nur weil der 
Wert in einer 32-Bit-Variable gespeichert sein muss. Es reicht auch ein 
halb so großer Puffer. Dass man diesen Wertebereich nicht verlässt, muss 
man dann natürlich sicherstellen.

von Wilhelm M. (wimalopaan)


Bewertung
-2 lesenswert
nicht lesenswert
Rolf M. schrieb:
> Wilhelm M. schrieb:
>> Nicht notwendigerweise. Der wichtige Punkt ist: wird die Schnittstelle
>> von itoa() falsch benutzt, d.h. hier: verwendet der Anwender einen zu
>> kleinen Buffer / Container als Output-Parameter für den zu wandelnden
>> Ganzzahltyp, dann darf das nicht kompilieren(!).
>
> Nicht "zu klein für den Typ", sondern "zu klein für den Wertebereich".
> Wenn  ich einen Wert im Bereich 0 bis 80000 in einen String wandeln
> will, brauche ich nicht einen 12 Bytes großen Puffer dafür, nur weil der
> Wert in einer 32-Bit-Variable gespeichert sein muss. Es reicht auch ein
> halb so großer Puffer. Dass man diesen Wertebereich nicht verlässt, muss
> man dann natürlich sicherstellen.

Ein klares Nein. Eine Funktion, die einen domänen-unspezifischen Typ wie 
etwa uint32_t in irgendwas wandeln soll, MUSS davon ausgehen, das der 
Wertebereich ausgenutzt wird. Bei solchen Datentypen gibt es keine 
"ungültigen" Werte aus Sicht der verarbeitenden Funktion.

Man kann das aber natürlich mit spezielleren Typ als die primitiven 
machen: etwa in der Art uint_bounded<uint32_t, 999>. Dafür reichen dann 
bei itoa() char-Container der Länge 3. Das kostet auch gar keine 
Laufzeit - das ist ja gerade die Stärke von C++, dass man 
domänen-spezifische DT "kostenneutral" bilden kann. Damit bildet man 
möglichst viel Semantik in den Typen ab, was der Compiler zur 
Compile-Zeit prüfen kann. Zusicherungen zur Laufzeit sind immer die 
zweite Wahl.

Natürlich ist uint_bounded<> auch noch ein unspezifischer DT. Bspw. für 
Kalendermonate sollte man dann etwas wie Date::Month haben, statt int.

Man kann natürlich eine entsprechende Funktion wie itoa() schreiben, der 
man beliebige, auch zur kurze Container übergibt. Dann muss man zur 
Laufzeit prüfen und ggf. abschneiden. Die würde ich dann aber anders 
nennen. Aber wichtig ist eben, das itoa_xxx() das prüfen kann. Und das 
kann sie als
1
void itoa_xxx(uint32_t, char*)
nicht, aber als
1
template<uint8_t L>
2
void itoa_xxx<uint32_t, array<char, L>&);
schon.

von Jens G. (jensg)


Bewertung
0 lesenswert
nicht lesenswert
Ungeachtet aller "Formfehler" bezüglich der Nutzung von C++, so sollte 
das Stück Software auch zuverlässig funktionieren. Das hat es leider 
nicht. Ich habe Fehler bereinigt und noch etwas Zeitoptimierung gemacht.
1
 Test: 900x itoa_() at 12_MHz on ATMEGA_1284p
2
 =========================
3
 0 -                    1 -
4
 1 -                   10 -
5
 2 -                  100 -
6
 3 -                 1000 -  48 ms (  53 µs )
7
 4 -                10000 -  51 ms (  57 µs )
8
 5 -               100000 -  81 ms (  90 µs )
9
 6 -              1000000 - 102 ms ( 113 µs )
10
 7 -             10000000 - 152 ms ( 169 µs )
11
 8 -            100000000 - 152 ms ( 169 µs )
12
 9 -           1000000000 - 201 ms ( 223 µs )
13
10 -          10000000000 - 249 ms ( 277 µs )
14
11 -         100000000000 - 275 ms ( 306 µs )
15
12 -        1000000000000 - 294 ms ( 327 µs )
16
13 -       10000000000000 - 300 ms ( 333 µs )
17
14 -      100000000000000 - 337 ms ( 374 µs )
18
15 -     1000000000000000 - 368 ms ( 409 µs )
19
16 -    10000000000000000 - 419 ms ( 466 µs )
20
17 -   100000000000000000 - 435 ms ( 483 µs )
21
18 -  1000000000000000000 - 485 ms ( 539 µs )
22
19 - 10000000000000000000 - 544 ms ( 604 µs )

von Wilhelm M. (wimalopaan)


Bewertung
0 lesenswert
nicht lesenswert
Habs auch mal auf einem ATMega1284p-Testboard (20Mhz) laufen lassen:
1
0 : V : 0,0ms
2
1 : V : 1,2ms
3
2 : V : 10,2ms
4
3 : V : 100,6ms
5
4 : V : 1000,19ms
6
5 : V : 10000,29ms
7
6 : V : 100000,50ms
8
7 : V : 1000000,79ms
9
8 : V : 10000000,81ms
10
9 : V : 100000000,109ms
11
10 : V : 1000000000,109ms
12
11 : V : 10000000000,154ms
13
12 : V : 100000000000,159ms
14
13 : V : 1000000000000,190ms
15
14 : V : 10000000000000,198ms
16
15 : V : 100000000000000,237ms
17
16 : V : 1000000000000000,266ms
18
17 : V : 10000000000000000,268ms
19
18 : V : 100000000000000000,318ms
20
19 : V : 1000000000000000000,319ms

Allerdings sind die Routinen nicht für jedermann geeignet, das sie wegen 
der LookUp-Tabelle 200 Byte RAM zusätzlich verbrauchen.

Eine primitiv-Variante erreicht:
1
0 : V : 0,0ms
2
1 : V : 1,10ms
3
2 : V : 10,17ms
4
3 : V : 100,24ms
5
4 : V : 1000,51ms
6
5 : V : 10000,63ms
7
6 : V : 100000,189ms
8
7 : V : 1000000,219ms
9
8 : V : 10000000,251ms
10
9 : V : 100000000,281ms
11
10 : V : 1000000000,313ms
12
11 : V : 10000000000,822ms
13
12 : V : 100000000000,951ms
14
13 : V : 1000000000000,1083ms
15
14 : V : 10000000000000,1214ms
16
15 : V : 100000000000000,1363ms
17
16 : V : 1000000000000000,1515ms
18
17 : V : 10000000000000000,1688ms
19
18 : V : 100000000000000000,1859ms
20
19 : V : 1000000000000000000,2050ms

ohne zusätzlichen RAM Verbrauch durch Lookup-Tabelle.

Space <-> Speed-Tradeoff

: Bearbeitet durch User
von Jens G. (jensg)


Bewertung
0 lesenswert
nicht lesenswert
Wilhelm M. schrieb:
> Allerdings sind die Routinen nicht für jedermann geeignet, das sie wegen
> der LookUp-Tabelle 200 Byte RAM zusätzlich verbrauchen.
>
Macht es Sinn die LookUp-Tabelle im Pgm-Space zu halten?

von Jens G. (jensg)


Bewertung
0 lesenswert
nicht lesenswert

von Wilhelm M. (wimalopaan)


Bewertung
0 lesenswert
nicht lesenswert
Habe meine Lösung nochmal umgebaut. Es ist nun einen generische 
Variante, die für alle Basen (2 ... 16) wandeln kann und eine 
Lookup-Tabelle verwendet (Space-Speed-Tradeoff).

Das sind die Zahlen (at328p@12MHz):
1
O:0 : V : ,0,0,0ms
2
O:1 : V : ,1,1,2ms
3
O:2 : V : ,1,10,4ms
4
O:3 : V : ,1,100,11ms
5
O:4 : V : ,2,1000,21ms
6
O:5 : V : ,2,10000,37ms
7
O:6 : V : ,3,100000,102ms
8
O:7 : V : ,3,1000000,147ms
9
O:8 : V : ,3,10000000,150ms
10
O:9 : V : ,3,100000000,195ms
11
O:10 : V : ,3,1000000000,198ms
12
O:11 : V : ,4,10000000000,258ms
13
O:12 : V : ,4,100000000000,304ms
14
O:13 : V : ,4,1000000000000,325ms
15
O:14 : V : ,4,10000000000000,365ms
16
O:15 : V : ,4,100000000000000,382ms
17
O:16 : V : ,4,1000000000000000,431ms
18
O:17 : V : ,4,10000000000000000,434ms
19
O:18 : V : ,4,100000000000000000,498ms
20
O:19 : V : ,4,1000000000000000000,500ms

Es ist eine vollständig reguläre Lösung für alle Datentypen (uint8_t ... 
uint64_t sowie signed) mit Längenbestimmung über binäre Auswahl, die mit 
templates realisiert ist.

von Jens G. (jensg)


Bewertung
0 lesenswert
nicht lesenswert
Wilhelm M. schrieb:
> Habe meine Lösung nochmal umgebaut. Es ist nun einen generische
> Variante, die für alle Basen (2 ... 16) wandeln kann und eine
> Lookup-Tabelle verwendet (Space-Speed-Tradeoff).
>
> Es ist eine vollständig reguläre Lösung für alle Datentypen (uint8_t ...
> uint64_t sowie signed) mit Längenbestimmung über binäre Auswahl, die mit
> templates realisiert ist.
>
Der Quelltext zu bekommen, währe nicht schlecht ;-) .

: Bearbeitet durch User
Beitrag #5007148 wurde vom Autor gelöscht.
von Wilhelm M. (wimalopaan)


Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Jens G. schrieb:
> Wilhelm M. schrieb:
>> Habe meine Lösung nochmal umgebaut. Es ist nun einen generische
>> Variante, die für alle Basen (2 ... 16) wandeln kann und eine
>> Lookup-Tabelle verwendet (Space-Speed-Tradeoff).
>>
>> Es ist eine vollständig reguläre Lösung für alle Datentypen (uint8_t ...
>> uint64_t sowie signed) mit Längenbestimmung über binäre Auswahl, die mit
>> templates realisiert ist.
>>
> Der Quelltext zu bekommen, währe nicht schlecht ;-) .

Hier der erste Wurf (s.a. Anhang):

von Wilhelm M. (wimalopaan)


Bewertung
0 lesenswert
nicht lesenswert
Jens G. schrieb:
> Wilhelm M. schrieb:
>> Habe meine Lösung nochmal umgebaut. Es ist nun einen generische
>> Variante, die für alle Basen (2 ... 16) wandeln kann und eine
>> Lookup-Tabelle verwendet (Space-Speed-Tradeoff).
>>
>> Es ist eine vollständig reguläre Lösung für alle Datentypen (uint8_t ...
>> uint64_t sowie signed) mit Längenbestimmung über binäre Auswahl, die mit
>> templates realisiert ist.
>>
> Der Quelltext zu bekommen, währe nicht schlecht ;-) .

Bei Deinem Code solltest Du unbedingt noch für die switch-cases ein 
fallthrough attribute ergänzen. Ansonsten bekommt einen Haufen von 
Warnungen ...

von Wilhelm M. (wimalopaan)


Bewertung
0 lesenswert
nicht lesenswert
Beim der Menge von Warnungen ist mir das in Deinem Code auch nicht 
aufgefallen: es gibt Funktionen, die haben ein switch-case mit 
fallthrough und ohne default. In diesem Fall gibt es die Warnung, dass 
ein return für diese non-void Funktion fehlt. Zwar kann der Fall (im 
Moment) in Deinem Code nicht vorkommen, doch das weiß ja der Compiler so 
nicht. Deswegen ein default-case einbauen mit einem assert(false) und 
auch ein return p; (falls das assert()-Macro eine noreturn-Funktion 
aufruft)

von Jens G. (jensg)


Bewertung
0 lesenswert
nicht lesenswert
Wilhelm M. schrieb:
> Beim der Menge von Warnungen ist mir das in Deinem Code auch nicht
> aufgefallen: es gibt Funktionen, die haben ein switch-case mit
> fallthrough und ohne default. In diesem Fall gibt es die Warnung, dass
> ein return für diese non-void Funktion fehlt.
danke für den Tip - default switch-case verwenden. Das hat den 
Maschinencode verringert und die Laufzeit etwas verbessert.
https://github.com/JensGrabner/snc98_Slash-Number-Calculator

von Johann L. (gjlayde) Benutzerseite


Bewertung
0 lesenswert
nicht lesenswert
Wilhelm M. schrieb:
> Lookup-Tabelle verwendet (Space-Speed-Tradeoff).
> Das sind die Zahlen (at328p@12MHz):
> O:19 : V : ,4,1000000000000000000,500ms

Irgendwas kann da doch nicht stimmen.  Bei 12MHz und 500ms wären das 
6.000.000 Cycles.  Eine Umwandlung nach ASCII Dezimal in in 1/1000 der 
Zeit machbar, und zwar ohne Splatz durch Lookup-Tabelle zu 
verschwenden.

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Bewertung
0 lesenswert
nicht lesenswert
Die Zeit gilt für 900 Konvertierungen (bitte lies weiter oben) wegen 
Vergleich zum Ausgangscode ...

: Bearbeitet durch User
von Jens G. (jensg)


Bewertung
0 lesenswert
nicht lesenswert
Wilhelm M. schrieb:
> Die Zeit gilt für 900 Konvertierungen (bitte lies weiter oben) wegen
> Vergleich zum Ausgangscode ...
in jede Dekade werden bei meinem Test 900 unterschiedliche Zahlen 
erzeugt. Ab der Zahl 100 ist es +1, ab 1000 ist es +10  ... das macht 17 
Dekaden bei dem vollen Zahlenumfang den ich teste  --  bei meiner 
Testroutine werden 15300 Zahlen erzeugt - so konnte ich Fehler finden.

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Bewertung
0 lesenswert
nicht lesenswert
Noch eine interessante Beobachtung
(für uint32_t, uint64_t geht mit ltoa() ja nicht mehr):
1
   text    data     bss     dec     hex filename
2
    252       0      87     339     153 bm02a.elf
3
    374       0      87     461     1cd bm03a.elf
4
    828     202      87    1117     45d bm04a.elf

bm02a: meine template Variante (auch die schnellste)
bm03a: aus avr-libc
bm04a: von jensg

(Bei den Beispielen oben ist jeweils noch eine Text-Ausgabe dabei):

von Johann L. (gjlayde) Benutzerseite


Bewertung
-1 lesenswert
nicht lesenswert
Wilhelm M. schrieb:
> Noch eine interessante Beobachtung
> (für uint32_t, uint64_t geht mit ltoa() ja nicht mehr):
>
>
1
>    text    data     bss     dec     hex filename
2
>     252       0      87     339     153 bm02a.elf
3
>     374       0      87     461     1cd bm03a.elf
4
>
>
> bm02a: meine template Variante (auch die schnellste)
> bm03a: aus avr-libc

Lass einfach mal "constexpr uint8_t Base = 10;" weg und übergib Base als 
Parameter, das mach den Vergleich mit der avr-libc etwas realistischer.

von Wilhelm M. (wimalopaan)


Bewertung
0 lesenswert
nicht lesenswert
Johann L. schrieb:
> Wilhelm M. schrieb:
>> Noch eine interessante Beobachtung
>> (für uint32_t, uint64_t geht mit ltoa() ja nicht mehr):
>>
>>
1
>>    text    data     bss     dec     hex filename
2
>>     252       0      87     339     153 bm02a.elf
3
>>     374       0      87     461     1cd bm03a.elf
4
>>
>>
>> bm02a: meine template Variante (auch die schnellste)
>> bm03a: aus avr-libc
>
> Lass einfach mal "constexpr uint8_t Base = 10;" weg und übergib Base als
> Parameter, das mach den Vergleich mit der avr-libc etwas realistischer.

Schau Dir den Code noch mal genauer an!

von Johann L. (gjlayde) Benutzerseite


Bewertung
0 lesenswert
nicht lesenswert
Wilhelm M. schrieb:
> Schau Dir den Code noch mal genauer an!

Unw was soll mir da auffallen?

von Jens G. (jensg)


Bewertung
0 lesenswert
nicht lesenswert
Hallo Wilhelm M. .. Arduino kennt nur eine Teilmenge von C++. Womit 
programmierst Du? Damit ich einen Weg zu Arduino finde, deinen Quelltext 
zu testen.

von Wilhelm M. (wimalopaan)


Bewertung
0 lesenswert
nicht lesenswert
Jens G. schrieb:
> Hallo Wilhelm M. .. Arduino kennt nur eine Teilmenge von C++. Womit
> programmierst Du? Damit ich einen Weg zu Arduino finde, deinen Quelltext
> zu testen.

Mit Arduino hat das ganze nichts zu tun ...

Du brauchst einen g++ mit variadic-templates und fold-expressions (ab 
Version 6) und concepts-lite (glaube ab Version 6.3.1 vorhanden, 
Schalter -fconcepts nicht vergessen) und einiges von C++17, was erst mit 
g++ >= 7.0 dabei ist, v.a. constexpr-lambda-expressions sowie 
Selektionen mit initializern, inline-static Variablen, constexpr-if und 
wahrscheinlich noch ein paar mehr Sachen aus c++17, die ich mir so 
angewöhnt habe (ich denke, structured bindings könnten auch dabei sein 
sowie variablen-templates).

von Jens G. (jensg)


Bewertung
0 lesenswert
nicht lesenswert
Wird dein Stück Software auf Atmel AVR 8-bit lauffähig sein? C++17 ist 
um einiges entfernt von C++11 der auf Atmel portiert wurde.

von Wilhelm M. (wimalopaan)


Bewertung
0 lesenswert
nicht lesenswert
Jens G. schrieb:
> Wird dein Stück Software auf Atmel AVR 8-bit lauffähig sein? C++17 ist
> um einiges entfernt von C++11 der auf Atmel portiert wurde.

Es IST auf AVR-8Bit lauffähig - und läuft. Sonst hätte ich Dir kaum die 
Zeiten und Code-Größen sagen können ... ;-)

Wie das mit ATmel-Studio oder Arduino-IDE geht, kann ich Dir allerdings 
nicht sagen. Als IDE bzw. Editor nehme ich QtCreator und den Rest 
erledigen ein paar Makefiles.

von Jens G. (jensg)


Bewertung
0 lesenswert
nicht lesenswert
Wilhelm M. schrieb:
> Jens G. schrieb:
>> Wird dein Stück Software auf Atmel AVR 8-bit lauffähig sein? C++17 ist
>> um einiges entfernt von C++11 der auf Atmel portiert wurde.
>
> Es IST auf AVR-8Bit lauffähig - und läuft. Sonst hätte ich Dir kaum die
> Zeiten und Code-Größen sagen können ... ;-)
>
Sorry, wollte Deine qualitativ hochwertige Arbeit nich schmälern. Ich 
selbst kann Deine Arbeit nicht bewerten, da ich in meinem 
Open-Source-Projekt Lösungen brauche die einfach auf der 
Arduino-Plattform zum laufen zu bringen sind. Wie das technisch auf 
Arduino geht -- da bin ich fachlich überfordert und werde deinen Ansatz 
nicht weiter verfolgen (können). Schade....

von Wilhelm M. (wimalopaan)


Bewertung
0 lesenswert
nicht lesenswert
Jens G. schrieb:
> Wilhelm M. schrieb:
>> Jens G. schrieb:
>>> Wird dein Stück Software auf Atmel AVR 8-bit lauffähig sein? C++17 ist
>>> um einiges entfernt von C++11 der auf Atmel portiert wurde.
>>
>> Es IST auf AVR-8Bit lauffähig - und läuft. Sonst hätte ich Dir kaum die
>> Zeiten und Code-Größen sagen können ... ;-)
>>
> Sorry, wollte Deine qualitativ hochwertige Arbeit nich schmälern. Ich
> selbst kann Deine Arbeit nicht bewerten, da ich in meinem
> Open-Source-Projekt Lösungen brauche die einfach auf der
> Arduino-Plattform zum laufen zu bringen sind. Wie das technisch auf
> Arduino geht -- da bin ich fachlich überfordert und werde deinen Ansatz
> nicht weiter verfolgen (können).

Du brauchst doch einfach nur eine neuere Version des avr-g++ zu 
installieren. Das ist alles. Es ist ja alles abwärtskompatibel bis zu 
C++98. Das Compilieren eines avr-gcc besteht doch nur aus

configure ...
make
make install

Und dann musst Du Deiner IDE nur sagen, dass sie einen anderen Compiler 
benutzen soll ...

von Frank M. (ukw) (Moderator) Benutzerseite


Bewertung
0 lesenswert
nicht lesenswert
Wilhelm M. schrieb:
> Und dann musst Du Deiner IDE nur sagen, dass sie einen anderen Compiler
> benutzen soll ...

Tja, und genau das ist die Frage, ob das bei Arduino so einfach geht. 
Konfigurierbar ist da herzlich wenig. Ich hatte auch schon mehrfach 
vergeblich versucht, lediglich ein paar Warning-Levels hochzuschrauben, 
weil sonst noch nichtmals unbenutzte Variablen angemeckert werden.

Bei manchen Dingen kann so ein Wohlfühlkissen furchtbar starr werden.

: Bearbeitet durch Moderator
von Jens G. (jensg)


Bewertung
0 lesenswert
nicht lesenswert
Frank M. schrieb:
> Wilhelm M. schrieb:
>> Und dann musst Du Deiner IDE nur sagen, dass sie einen anderen Compiler
>> benutzen soll ...
>
> Tja, und genau das ist die Frage, ob das bei Arduino so einfach geht.
> Konfigurierbar ist da herzlich wenig.
Da ich ein Open-Source-Projekt für Arduino betreibe, bin ich darauf 
angewiesen, alles so zu machen, das es bei Arduino geht. Was ein Vorteil 
für den einen (Anfänger) ist, ist ein Nachteil für den anderen (Profi). 
Arduino verwendet aktuell: avr-gcc=4.9.2-atmel3.5.4-arduino2.

von Wilhelm M. (wimalopaan)


Bewertung
0 lesenswert
nicht lesenswert
Jens G. schrieb:
> Frank M. schrieb:
>> Wilhelm M. schrieb:
>>> Und dann musst Du Deiner IDE nur sagen, dass sie einen anderen Compiler
>>> benutzen soll ...
>>
>> Tja, und genau das ist die Frage, ob das bei Arduino so einfach geht.
>> Konfigurierbar ist da herzlich wenig.
> Da ich ein Open-Source-Projekt für Arduino betreibe, bin ich darauf
> angewiesen, alles so zu machen, das es bei Arduino geht. Was ein Vorteil
> für den einen (Anfänger) ist, ist ein Nachteil für den anderen (Profi).
> Arduino verwendet aktuell: avr-gcc=4.9.2-atmel3.5.4-arduino2.

Oh je: Version 4.9.2 ist aber wirklich alt (vor Juni 2015).

Gerade bei C++ hat sich ja in dieser Zeit SEHR viel geändert, vor allem 
in Bezug auf templates und constexpr, was man für µC sehr gut gebrauchen 
kann. Und es wird sich in Zukunft ja weiterhin viel ändern!

Da kann ich nur dringend empfehlen, mal zu schauen, ob man dass nicht 
ändern kann.

von Jens G. (jensg)


Bewertung
0 lesenswert
nicht lesenswert
Es ist schon etwas länger her  --  jetzt gibt es auch ein Format int96_a 
der auf 8bit Atmel rechnen kann  ...  und das "itoA" von mir kann es 
auch nach ASCII umwandeln.

Infos zum int96_a gibt es hier:
#include <int96.h>
// Original:  http://www.naughter.com/int96.html
// 
https://github.com/JensGrabner/snc98_Slash-Number-Calculator/tree/master/Software/Arduino/libraries/int96

#include <itoa_ljust.h>
// 
https://github.com/JensGrabner/snc98_Slash-Number-Calculator/tree/master/Software/Arduino/libraries/itoa_ljust

Ein Testprogramm findet man hier:
https://github.com/JensGrabner/snc98_Slash-Number-Calculator/tree/master/Software/Arduino/Projekte/Test_itoa_ljust

Es sind damit Rechungen bis zu 28 Dezimalstellen möglich.

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Bewertung
0 lesenswert
nicht lesenswert
Habe gerade einen ganz kurzen Blick da hinein geworfen. Sehe ich das 
richtig, dass Du den Code unverändert übernommen hast?

von Jens G. (jensg)


Bewertung
0 lesenswert
nicht lesenswert
Wilhelm M. schrieb:
> Habe gerade einen ganz kurzen Blick da hinein geworfen. Sehe ich das
> richtig, dass Du den Code unverändert übernommen hast?
Es kommt drauf an, auf was es sich bezieht - ja < Original: 
http://www.naughter.com/int96.html > habe ich weitgehend übernommen. Der 
Quallcode ist auf die .. #include <inttypes.h> Konvention angepasst.

Und .. #include <itoa_ljust.h> habe ich von 64bit auf 96bit erweitert. 
Es besteht noch Optimierungsbedarf - ab bit 65 wird es 10x langsamer.

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Bewertung
0 lesenswert
nicht lesenswert
Jens G. schrieb:

> Und .. #include <itoa_ljust.h> habe ich von 64bit auf 96bit erweitert.
> Es besteht noch Optimierungsbedarf - ab bit 65 wird es 10x langsamer.

Ok, der Frage war einfach nur erst einmal reines Interesse.

Bezogen auf die Schnittstelle und auch Teile der Implementierung sehe 
ich auch noch Verbesserungsmöglichkeiten. Es ist teilweise nicht sehr 
idiomatisch und die Typumwandlungsoperation halte ich für problemtisch 
...

von Jens G. (jensg)


Bewertung
0 lesenswert
nicht lesenswert
Mein Quelltext ist verbessert worden. Die einzig große Division mit 
int96 habe ich durch eine Multiplikation ersetzt (* 1/x). Dazu habe ich 
eine neue Funktion erstellt (a * b) / 2^95.
1
void mul_div95(const int96_a& mul, int96_a& rVal) const;

https://github.com/JensGrabner/snc98_Slash-Number-Calculator/blob/master/Software/Arduino/libraries/itoa_ljust/result/test_int96_a.txt

von Jens G. (jensg)


Bewertung
0 lesenswert
nicht lesenswert
Ich habe den Quelltext aktuallisiert. Es waren Fehler in der Umwandlung 
von int96_a drinn -- jetzt dauert es etwas länger in der Zeit (max. 1.3 
ms).

von Johann L. (gjlayde) Benutzerseite


Angehängte Dateien:

Bewertung
1 lesenswert
nicht lesenswert
Auch wenn dieser Thread schon etwas älter ist...

Beim Durchsehen meiner Platte hab ich eine ältere Konvertierungsroutine 
in AVR-Assembler gefunden.  Geht also in die andere Richtung als die C++ 
Fraktion.  Weil so eine Konvertierung als low-Level angesehen werden 
kann, ist Assembler jedenfalls nicht komplett abwegig.

Vielleicht kann jemand was damit anfangen.  Ich hab paar Kommentare 
ergänzt und einen C/C++ Header dazu gemacht so dass es einfacher von 
C/C++ aus zu verwenden ist.
1
char* ulltoa_base10 (uint64_t value, char *buf);
2
char* lltoa_base10 (int64_t value, char *buf);
3
4
/* Slower than expected.  Reading from table takes its toll... */
5
char* ulltoa_base10_fast (uint64_t value, char *buf);

Hier ein paar Metriken für ulltoa_base10:

* Weniger als 200 Bytes Code, keine Loopup-Tabelle.
* Worst-Case Execution Time unter 3300 Cycles.
* Worst-Case Execution Time unter 2800 Cycles (mit MUL).
* Average Execution Time unter 2000 Cycles (mit MUL).

Rückgabe ist die Adresse des '\0' am Stringende, so dass man leicht 
andere Strings anhängen kann.  Die Stringlänge ist so einfach per 
Zeiger-Differenz bestimmbar.

Umgerechnet auf die Werte von oben mit 900 Runden @ 12MHz sind das 
250ms, 210ms und 150 ms.  Bezogen auf 20MHz sind es 150ms, 130ms und 
90ms.

Jens G. schrieb:
> Ich habe Fehler bereinigt und noch etwas Zeitoptimierung gemacht.
1
 Test: 900x itoa_() at 12_MHz on ATMEGA_1284p
2
> 17 -   100000000000000000 - 435 ms ( 483 µs )
3
> 18 -  1000000000000000000 - 485 ms ( 539 µs )
4
> 19 - 10000000000000000000 - 544 ms ( 604 µs )

Wilhelm M. schrieb:
> Habs auch mal auf einem ATMega1284p-Testboard (20Mhz) laufen lassen:
1
> 17 : V : 10000000000000000,268ms
2
> 18 : V : 100000000000000000,318ms
3
> 19 : V : 1000000000000000000,319ms
> Allerdings sind die Routinen nicht für jedermann geeignet, das sie wegen
> der LookUp-Tabelle 200 Byte RAM zusätzlich verbrauchen.

Auch eine Version mit Lookup-Tabelle ist dabei.  Was mich etwas 
erstaunte, ist dass diese gegenüber der Version ohne Tabelle nur einen 
marginalen Zuwachs an Geschwindigkeit brachte.

Bestimmt wurden die Zeiten mit dem avrtest Simulator.  Der unterstützt 
Performance-Messung.

ulltoa_base10() braucht weder Lookup noch Division oder Mudulo und 
beginnt die Umwandlung mit den höherwertigen Ziffern.  Das Verfahren ist 
daher einfach auf Fixed-Point übertragbar.

Anbei sind auch 2 Implementierungen in C:
1
/* A C analogon of ulltoa_base10 to show the basic principle.  */
2
char* ulltoa_base10_C (uint64_t x, char *buf);
3
4
/* A simple but also typically veeery slooow C implementation.  */
5
char* u64toa_base10_C (uint64_t x, char *buf);

von Jens G. (jensg)


Bewertung
0 lesenswert
nicht lesenswert
Es freut mich, das dieser Thread wieder etwas belebt wird.

Ich werde bald ein anderes Zahlenformat benötigen.
128-bit (64.64) signed fixed-point arithmetic.
https://github.com/fahickman/r128

Da dürfte es sinnvol sein, für dieses Format meine Routinen zu 
erweitern.

: Bearbeitet durch User
von c++ ftw (Gast)


Bewertung
0 lesenswert
nicht lesenswert
In C++ gibt es jetzt to_chars und from_chars, die besser als alle 
bisherigen string conversions der c/c++ lib für Mikrocontroller geeignet 
sind.

Wäre interessant wie die sich im Platz- und Geschwindigkeitsvergleich 
schlagen.

von Jens G. (jensg)


Bewertung
-1 lesenswert
nicht lesenswert
Diese Funktionen sind noch sehr neu und dürften auf 8bit Arduino nur 
schwer zum Laufen zu bringen sein.

https://en.cppreference.com/w/cpp/utility/to_chars

von Johann L. (gjlayde) Benutzerseite


Bewertung
1 lesenswert
nicht lesenswert
c++ ftw schrieb:
> In C++ gibt es jetzt to_chars und from_chars, die besser als alle
> bisherigen string conversions der c/c++ lib für Mikrocontroller geeignet
> sind.

Ist erst mal nur ein Interface, das effiziente Implementierung 
prinzipiell ermöglicht, weil keine dynamische Speicherallikierung 
notwendig und keine fancy Formatierungen.

Wenn es bislang noch keine effiziente Implementierung von ultoa / utoa 
gab, wie sie zum Beispiel die avr-libc enthält, dann wird die neue 
Fassade auch nicht dazu führen, dass Effizient plötzlich vom Himmel 
fällt.  Und wenn, kann das gleiche Verfahren für ultoa etc. verwendet 
werden: Ein Integer Typ, dessen String-Darstellung in einer Basis 2...36 
zu bestimmen ist.

von Veit D. (devil-elec)


Bewertung
0 lesenswert
nicht lesenswert
Hallo,

wie kann man die charconv einbinden? Ich habe Dank Johann einen frisch 
compilierten avr-gcc 9.1 zur Verfügung. (seit paar Tagen gibts 9.2) Die 
charconv Lib kennt er jedoch nicht. Was tun?

von Jens G. (jensg)


Bewertung
0 lesenswert
nicht lesenswert
Hier ist offensichtlich der Quelltext: 
https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/std/charconv
Ob dies bei einer 8-bit CPU sinvoll ist? - Es wird viel Speicherplatz 
für Konstanten benötigt. Mein öffentlichter Quelltext funktioniert. 
Integer_to_Ascii

: Bearbeitet durch User
von Oliver S. (oliverso)


Bewertung
0 lesenswert
nicht lesenswert
Veit D. schrieb:
> Die
> charconv Lib kennt er jedoch nicht. Was tun?

Du könntest mal die fehlende C++-Standardlib für AVR schreiben...

Oliver

: Bearbeitet durch User
von Jens G. (jensg)


Bewertung
0 lesenswert
nicht lesenswert
Oliver S. schrieb:
> Du könntest mal die fehlende C++-Standardlib für AVR schreiben...
ich lese bei Zeile 1: Primitive numeric conversions (to_chars and 
from_chars)

Meine öffentliche Quelle kann "nur" .. to_chars.

Mir ist nicht bekannt, wie man dort mitarbeiten kann.

von Veit D. (devil-elec)


Bewertung
0 lesenswert
nicht lesenswert
Hallo,

die charconv benötigt noch andere fehlende Libs. Ich dachte naiv man 
könnte die Libs vielleicht beim Toolchain bauen mit einbinden. Habe ich 
wohl falsch gedacht.

Wegen der STL schreiben. Wenn ich groß bin und C++ komplett beherrsche 
könnte ich damit anfangen. Allerdings befürchte ich das mich die C++ 
Entwicklung immer überholen wird. :-)

Mal sachlich gefragt. Was ist das Problem an der avr Anpassung der STL? 
Ich meine es gab ja schon welche, nur sind die für aktuelle avr-gcc 
veraltet. Wofür fürchten sich alle?   :-)

Bevor ich diesen Thread entdeckte habe ich mir sowas geschrieben. Damit 
kann ich alles bis 32Bit ausgeben. Ohne Funktion überladen zu müssen.
1
template <class T>
2
void print(const T var)
3
{
4
  char buffer[11];
5
  if (var < 0) {
6
    ltoa(var, buffer, 10);
7
  }
8
  else {
9
    ultoa(var, buffer, 10);
10
  }
11
  putS(buffer);
12
}

von Rolf M. (rmagnus)


Bewertung
0 lesenswert
nicht lesenswert
Veit D. schrieb:
> Mal sachlich gefragt. Was ist das Problem an der avr Anpassung der STL?
> Ich meine es gab ja schon welche, nur sind die für aktuelle avr-gcc
> veraltet. Wofür fürchten sich alle?   :-)

Fürchten wird sich keiner davor. Es gibt wohl nur keinen, der sowohl 
Lust, als auch das Wissen hat, um es zu machen.
Wahrscheinlich warten alle, "bis sie groß sind und C++ komplett 
beherrschen" ;-)
Dazu kommt, dass große Teile der Standardbibliothek massiv auf 
dynamischen Speicher setzen, den man auf kleinen AVRs eher sparsam bis 
gar nicht verwendet. Eine std::map dürfte dort selten sinnvoll sein.

von Veit D. (devil-elec)


Bewertung
0 lesenswert
nicht lesenswert
Hallo Rolf,

na wenigstens Einer der eine gesunde Portion Humor mitbringt.
Gut ich verstehe das Problem. Ich dachte wenn man die Möglichkeiten der 
STL auf einem kleinen AVR mit Bedacht einsetzt, dann könnte man 
wenigstens ein paar wenige Teile zusätzlich nutzen. Man muss nur - wie 
immer - wissen was man tut.

Danke für die Antwort.

von Wilhelm M. (wimalopaan)


Bewertung
0 lesenswert
nicht lesenswert
Veit D. schrieb:
> Hallo Rolf,
>
> na wenigstens Einer der eine gesunde Portion Humor mitbringt.
> Gut ich verstehe das Problem. Ich dachte wenn man die Möglichkeiten der
> STL auf einem kleinen AVR mit Bedacht einsetzt, dann könnte man
> wenigstens ein paar wenige Teile zusätzlich nutzen. Man muss nur - wie
> immer - wissen was man tut.

Die C++-Standard-Lib (STL war einmal) auf kleine µC zu portieren, ist 
grundsätzlich nicht schwer. Nur ist es so, dass man bspw. die 
dynamischen Container bei den üblichen Problemstellungen dieser 
Kategorie µC gar nicht benötigt. Auch bei anderen Dingen wünscht man 
sich Anpassungen, die noch stärker in Richtung statische Polymorphie 
gehen.
Einige der Algorithmen kann man natürlich direkt übernehmen, wenn man 
sie braucht. Für meinen Teil habe ich das gemacht. So habe ich zwar 
keine C++-Sandard-lib, aber doch die Teile, die ich wirklich brauche. 
Allerdings auch noch mehr, z.B. eine TMP-Bibliothek (was meines 
Erachtens der Schlüssel zum Erfolg im Small-Embedded ist).
Also: es macht keiner / wird keiner machen, weil man es in der 
vorgegebenen Form nicht braucht. Und wenn ich mir diese Diskussionen um 
C++ im µC (zumindest in diesem Forum) ansehe, dann scheint auch der 
"Markt" gar nicht da zu sein - jedenfalls nicht hier.

von A. B. (Gast)


Bewertung
2 lesenswert
nicht lesenswert
Wilhelm M. schrieb:
> wenn ich mir diese Diskussionen um
> C++ im µC (zumindest in diesem Forum) ansehe, dann scheint auch der
> "Markt" gar nicht da zu sein - jedenfalls nicht hier.

Genau das ist der Punkt.
***********************
C++ ist abstrakt, leistungsfähig und dynamisch. Es ist nicht einfach in 
C++ mitzuschwimmen. Man muss sich mit C++ befassen. Das braucht Energie, 
Zeit und Durchhaltevermögen.

Schade, dass sich die Diskussionen hier rund um uC und C++ regelmässig 
festfahren.

Eine Plattform für Diskussionen, in der sich Interessierte und Experten 
konstruktiv über C++ für uC austauschen könnten, ist mikrocontroller.net 
leider nicht. Eine andere, geeignetere habe ich noch nicht gefunden.

Greenhorns schreiben manchmal, ohne sich in die Problematik einzudenken 
und Experten antworten manchmal auf einem Level, das eher abschreckt als 
motiviert ...

Zurück zum Thema
****************
Es ist durchaus möglich, im Netz moderne und leistungsfähige C++ 
Bibliotheken für uC zu finden ...

Habe vor längerer Zeit auf Basis Mcucpp eine VisualC++Console 
application zum ausprobieren erstellt.

Als Beispiel auf Wandbox implementiert. Ist zusammenkopiert, aber 
lauffähig und prinzipiell uC tauglich.

https://wandbox.org/permlink/j9bXlNkcy45E1j9Y

von Veit D. (devil-elec)


Bewertung
0 lesenswert
nicht lesenswert
Hallo,

wenn ich einzelne Teile der C++-Standard-Lib probieren möchte, in 
welchen Ordner der Toolchain müssen die Headerfiles einsortiert werden?

von Wilhelm M. (wimalopaan)


Bewertung
0 lesenswert
nicht lesenswert
Veit D. schrieb:
> Hallo,
>
> wenn ich einzelne Teile der C++-Standard-Lib probieren möchte, in
> welchen Ordner der Toolchain müssen die Headerfiles einsortiert werden?

Wo es Dir beliebt: die Option -I ist Dein Freund.

von Veit D. (devil-elec)


Bewertung
0 lesenswert
nicht lesenswert
Hallo,

Jens, ich habe mir erlaubt in switch case ein falltrough einzubauen. 
Sonst läuft mein Compiler Amok.  :-)
Habs ausnahmsweise auf die gleiche Zeile geschrieben damit der Aufbau 
optisch nicht unterbrochen wird.
1
    static inline char* itoa_16(uint16_t u, char* p, uint16_t d, uint8_t n) {
2
        switch(n) {
3
        case  5: u -= d * 10000;            [[fallthrough]];
4
        case  4: d  = u /   100; p = out( dd(d), p );  [[fallthrough]];
5
        case  3: u -= d *   100;            [[fallthrough]];
6
        case  2: n = 2 ;                [[fallthrough]];
7
        default: return itoa_8( u, p, d, n );
8
        }
9
    }
10
11
    static inline char* itoa_32(uint32_t u, char* p, uint32_t d, uint8_t n) {
12
        switch(n) {   // 1000000000
13
        case 10: d  = u / 100000000; p = out( dd(d), p );  [[fallthrough]];
14
        case  9: u -= d * 100000000;            [[fallthrough]];
15
        case  8: d  = u /   1000000; p = out( dd(d), p );  [[fallthrough]];
16
        case  7: u -= d *   1000000;            [[fallthrough]];
17
        case  6: d  = u /     10000; p = out( dd(d), p );  [[fallthrough]];
18
        case  5: u -= d *     10000;            [[fallthrough]];
19
        case  4: n = 4 ;                  [[fallthrough]];
20
        case  3: ;                      [[fallthrough]];
21
        case  2: ;                      [[fallthrough]];
22
        default: return itoa_16( u, p, d, n );
23
        }
24
    }
25
26
    static inline char* itoa_64(uint64_t u, char* p, uint64_t d, uint8_t n) {
27
        switch(n) {    // 1000000000000000000
28
        case 18: d  = u /   10000000000000000; p = out( dd(d), p );  [[fallthrough]];
29
        case 17: u -= d *   10000000000000000;            [[fallthrough]];
30
        case 16: d  = u /     100000000000000; p = out( dd(d), p );  [[fallthrough]];
31
        case 15: u -= d *     100000000000000;            [[fallthrough]];
32
        case 14: d  = u /       1000000000000; p = out( dd(d), p );  [[fallthrough]];
33
        case 13: u -= d *       1000000000000;            [[fallthrough]];
34
        case 12: d  = u /         10000000000; p = out( dd(d), p );  [[fallthrough]];
35
        case 11: u -= d *         10000000000;            [[fallthrough]];
36
        case 10: d  = u /           100000000; p = out( dd(d), p );  [[fallthrough]];
37
        case  9: u -= d *           100000000;            [[fallthrough]];
38
        case  8: n = 8;                        [[fallthrough]];
39
        case  7: ;                          [[fallthrough]];
40
        case  6: ;                          [[fallthrough]];
41
        case  5: ;                          [[fallthrough]];
42
        case  4: ;                          [[fallthrough]];
43
        case  3: ;                          [[fallthrough]];
44
        case  2: ;                          [[fallthrough]];
45
        default: return itoa_32( u, p, d, n ); 
46
      }
47
    }

Der Testsketch gibt am Ende eine seltsame Schlusszeile aus. Ich weiß 
noch nicht ob es nur an der Formatierung liegt oder etwas anderes Schuld 
ist.
1
Time: 73 ms 1000
2
Time: 77 ms 10000
3
Time: 102 ms 100000
4
Time: 124 ms 1000000
5
Time: 163 ms 10000000
6
Time: 166 ms 100000000
7
Time: 200 ms 1000000000
8
Time: 246 ms 10000000000
9
Time: 273 ms 100000000000
10
Time: 299 ms 1000000000000
11
Time: 307 ms 10000000000000
12
Time: 342 ms 100000000000000
13
Time: 380 ms 1000000000000000
14
Time: 419 ms 10000000000000000
15
Time: 445 ms 100000000000000000
16
Time: 484 ms 1000000000000000000
17
Time: 537 ms 10000000000000000000
18
Time: 825 ms 100000000000000000000
19
Time: 851 ms 1000000000000000000000
20
Time: 855 ms 10000000000000000000000
21
Time: 879 ms 100000000000000000000000
22
Time: 901 ms 1000000000000000000000000
23
Time: 940 ms 10000000000000000000000000
24
Time: 943 ms 100000000000000000000000000
25
Time: 979 ms 1000000000000000000000000000
26
Time: 1017 ms 10000000000000000000000000000
27
Time: 342 ms 39600000000000000000000000000

Wofür ist die 96Bit Funktion genau? Beim Atmega ist doch mit 64Bit 
Datentyp Schluss wenn ich mich nicht irre?
Im Testsketch kannste noch die includes von int96 und stdint weglassen, 
inkludierste schon im Header.
Sind nur freundlich gemeinte Hinweise und Fragen zum Verständnis zum 
drumherum.
Ansonsten Danke dafür.  Dem "Vater" Arturo Martin-de-Nicolas natürlich 
auch.

Wilhelm, dein Einwand mit to_string(i) ist zwar korrekt, nur ist string 
mal wieder nicht standardmäßig vorhanden. Das alte Problem. :-)
Danke für den Optionshinweis.

von Wilhelm M. (wimalopaan)


Bewertung
0 lesenswert
nicht lesenswert
Veit D. schrieb:
> Hallo,
>
> Jens, ich habe mir erlaubt in switch case ein falltrough einzubauen.
> Sonst läuft mein Compiler Amok.  :-)

Der Hinweis kam schon vor mehr als 2 Jahren ;-)

s.a. Beitrag "Re: ItoA mit Uint64_t"


> Wilhelm, dein Einwand mit to_string(i) ist zwar korrekt, nur ist string
> mal wieder nicht standardmäßig vorhanden. Das alte Problem. :-)
> Danke für den Optionshinweis.

Das bezog sich ja auch nur auf den Einwand mit dem std::stringstream 
(vor mehr als 2 Jahren). Die heutige Antwort wäre std::to_chars bzw. 
diese wäre eine Stelle, wo ich eben die cppstdlib nicht nachahme würde 
sondern statisch dimensionierte Strings / StringBuffer einsetze (s.a. 
mein Einwand von oben, dass es aus verschiedenen Gründen nicht sinnvoll 
ist, die cppstdlib im small-embedded-Bereich einfach nur kopieren zu 
wollen.

von Jens G. (jensg)


Bewertung
0 lesenswert
nicht lesenswert
Veit D. schrieb:
> Time: 1017 ms 10000000000000000000000000000
> Time: 342 ms 39600000000000000000000000000

Überschreitung des Zahlenformates
die höchste Zahl ist: 2^95 - 1 = 39614081257132168796771975167

Ich habe einen eigenen Zahlentyp in meine Routinen eingepflegt. .. weil 
ich das brauchen konnte.

Es beruht auf .. http://www.naughter.com/int96.html
meine Umsetzung dann: 
https://github.com/JensGrabner/snc98_Slash-Number-Calculator/tree/master/Software/Arduino/libraries/int96

von Veit D. (devil-elec)


Bewertung
0 lesenswert
nicht lesenswert
upps, aufs Datum hatte ich gar nicht geachtet.

Danke Euch, soweit ist erstmal alles klar.

von Johann L. (gjlayde) Benutzerseite


Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Wilhelm M. schrieb:
> Veit D. schrieb:
>> Hallo,
>>
>> wenn ich einzelne Teile der C++-Standard-Lib probieren möchte, in
>> welchen Ordner der Toolchain müssen die Headerfiles einsortiert werden?
>
> Wo es Dir beliebt: die Option -I ist Dein Freund.

In dem Falle -isystem, da es System-Header sind.

Ein paar Header hatte ich mal angepasst, um initializer_list und 
type_traits zu haben, die ganze STL oder gar libstdc++ ist es natürlich 
nicht — ob das sinnvoll auf AVR wäre, sei dahingestellt.  Die Header 
sind von einem host-g++ v4.7 abgeleitet, also schon was älter.

Generell kann man sagen, dass die Portierung um so aufwändiger und 
unangenehmer wird, je weiter man in Richtung low-level geht.  Konkret: 
Die libstc++-v3 dürfte leicht oder ganz ohne Aufwand zu portieren sein — 
bis auf den Basis-Support der libsupc++ und "Feinheiten" im Compiler wie 
Exception-Handling.

Ob das aktuelle nicht-Standard double (32-Bit) Probleme machen würde, 
kann ich nicht sagen, dito für den eigenwilligen Ansatz der avr-libc, 
float-Funktionen zu implementieren wie:
1
extern double log(double __x) __ATTR_CONST__;
2
#define logf  log    /**< The alias for log().  */

Wenn mal also eine Funktion / Template o.ä. mit logf verwenden will, 
dann verwendet man ohne es zu merken die double-Version. Besonders 
heimtückisch wenn man eine Klasse implementiert, die 
double-Funktionalität liefert.

Der beste Weg ist m.E.
1
__ATTR_CONST__ extern double log (double);
2
__ATTR_CONST__ extern float logf (float) __asm ("log"); /**< The alias for log(). */
So dann man korrekte Identifier und Prototypen ohne extra Call-Overhead 
hat.  Bzw noch besser wäre
1
__ATTR_CONST__ extern float logf (float);
2
__ATTR_CONST__ extern double log (double) __asm ("logf"); /**< The alias for logf(). */

Die Umstellung des Compiler auf 64-Bit double ist hingegen leicht 
machbar — beim Linken einer Applikation bekäme man einfach nur undefined 
References vom Linker z.B. für __adddf3 (Addition) die man dann selbst 
zur Verfügung stellen könnte.

GCC hat 2 unterschiedliche Implementationen für 
Soft-Float/Double/LongDouble, ob die sinnvoll sind... würd ich eher auf 
"wenig" plädieren.

Ich hab hier eine double-Implementierung in Assembler/C/C++ die soweit 
gut funktioniert; ist allerding nur die Basis-Ausstattung 
(Konvertierung, Vergleiche, +, -, *, /, floor, round, ceil, ldexp, sqrt, 
log, exp).  Wenn ich Zeit hab kommen als nächsten asin, acos und atan 
und atan2.


A. B. schrieb:
> Experten antworten manchmal auf einem Level, das eher abschreckt
> als motiviert ...

Amen.

von Veit D. (devil-elec)


Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ich würde deine Lib gern ausprobieren. Stehe jedoch vor dem Problem, 
dass ich nicht weiß wo ich den Eintrag in AS machen soll? Im Optionsfeld 
-I funktioniert es nicht.

von A. B. (Gast)


Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Es gibt keinen Grund, warum das nicht gehen sollte.
Ist ja c++ header only.

In | AS| SolutionExplorer(*) | Toolchain  |AVR-GNU_c++_complier | 
Directories |

mit dem Icon |+|   einen neuen Pfad anlegen
mit dem Icon |...| Datei Explorer öffnen und den Include Pfad suchen und 
eintragen.

(*) falls unsichtbar in VIEW sichtbar machen

von Veit D. (devil-elec)


Bewertung
0 lesenswert
nicht lesenswert
Hallo,

Danke. Scheint zu funktionieren, es meckert nichts.  :-)
Hatte bestimmt wieder irgendwas vermehrt.
Jetzt muss ich schauen was man damit so machen kann ...

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.