Go Down

Topic: [Projekt] Ausgabe von 64 Bit Werten  (Read 321 times) previous topic - next topic

combie

Aug 12, 2018, 01:28 pm Last Edit: Aug 13, 2018, 08:10 am by combie
Hi

In letzter Zeit häufen sich Problemstellungen, wo 64 Bit breite Values ausgegeben werden sollen.

Leider können unsere ganzen Wandler Funktionen dieses nicht leisten.
Auch Serial.print() nicht.

Drum habe ich mal meine Wühlkiste durchsucht, denn das Problem hatte ich auch schon mal.



Die Wühlkiste bietet die Funktion:
> char* bigToStr(char* buffer, auto value, uint8_t base = 10)

"buffer" ist ein Array of char, MUSS mindestens die Größe BufferSize haben.
Der Buffer muss von der Anwendung zur Verfügung gestellt werden.

"value" ist der Wert, welcher in einen String verwandelt werden will.
Es funktionieren beliebige integer Datentypen, bis incl. 64 Bit Typen.
Auch Vorzeichenbehaftete.

"base" ist die Zahlenbasis, die Grundlage der Wandlung.
Optimierungen für Binär, Oktal und Hexadezimal sind eingebaut.
Andere Zahlenbasen, wie z.B. Dezimal, sind recht rechenintensiv.
Base muss >=2 sein.
Base darf so groß werden, wie es der Zeichenvorrat zulässt.

Fehler bei der Festlegung der Base, oder Bufferoverflows, werden nicht abgefangen.


Rückgabe:
Ein Zeiger in den Buffer, auf den Anfang der generierten Zeichenkette.


Code: [Select]

unsigned long long test = 0xffffffffffffffffULL;

const size_t BufferSize = 64+1+1; // 64bit +Vorzeichen +NullByte
char buffer[BufferSize];


char* bigToStr(char* buffer, auto value, uint8_t base = 10)
{
  bool negative = value < 0;
  if(negative) value *= -1LL;

  char* ptr = buffer+BufferSize;
  *(--ptr)  = 0;
  switch(base)
  {

    case  2: while(value) // BINARY
             {
               *(--ptr)  = '0' + (value & 0x01);
               value >>= 1;
             }
             break;
             
    case  8: while(value) // OKTAL
             {
               *(--ptr)  = '0' + (value & 0x07);
               value >>= 3;
             }
             break;
             
    case 16: while(value) // HEX
             {
               *(--ptr)  = '0' + (value & 0x0f);
               if(*ptr > '9') *ptr += 'A'-'9'-1;
               value >>= 4;
             }
             break;
           
    default: while(value) // UNIVERSAL
             {
               uint8_t rest = value % base;
               *(--ptr)  = '0' + rest;
               if(*ptr > '9') *ptr += 'A'-'9'-1;
               value /= base;
             }
             break;
  }
  if(negative)  *(--ptr)  = '-';
  return ptr;
}



void setup()
{
  Serial.begin(9600);
  Serial.println("Start");
  for(int i=0;i<8;i++)Serial.print("0123456789");
  Serial.println("");
 
 Serial.println(bigToStr(buffer,test, 2));
 Serial.println(bigToStr(buffer,test, 8));
 Serial.println(bigToStr(buffer,test,16));
 Serial.println(bigToStr(buffer,144,12));
 Serial.println(bigToStr(buffer,-144));
 Serial.println(bigToStr(buffer,123456789ULL));
 Serial.println(bigToStr(buffer,-123456789LL));

}

void loop()
{

}


Viel Spass damit!
:o :o :o :o

EDIT:
Verwendet bitte die Template Version aus Posting #2, die sollte mit jedem C++11 Kompiler funktionieren

Kluge Worte zu schreiben, ist schwer.
Schon ein einziger Buchstabendreher, kann alles urinieren.

agmue

Eine Fehlermeldung verdirbt mir den Spass:

Test_Forum:6: error: parameter declared 'auto'
Test_Forum.ino: In function 'char* bigToStr(char*, uint8_t)':
Test_Forum:8: error: 'value' was not declared in this scope
parameter declared 'auto'

Wo steckt der Wurm oder ist IDE 1.6.5 zu alt?

combie

#2
Aug 12, 2018, 08:02 pm Last Edit: Aug 14, 2018, 11:47 am by combie
Quote
E:\Programme\arduino\portable\sketchbook\64BitPrint\64BitPrint.ino:9:28: warning: use of 'auto' in parameter declaration only available with -std=c++1y or -std=gnu++1y

 char* bigToStr(char* buffer, auto value, uint8_t base = 10)
Das stimmt...
In Version 1.8.5 ist gnu++11 die Standardeinstellung.
Und dann läuft das auch.
Ein Update würde also helfen.

Ich selber kompiliere mit gnu++14
(da bleibt mir dann auch die Warnung erspart)

3 Möglichkeiten sehe ich:
1. Update
2. Die betreffende platform.txt anpassen
3. ich mache ein Template draus


Weg 3:
Code: [Select]


unsigned long long test = 0xffffffffffffffffULL;

const size_t BufferSize = 64+1+1; // 64bit +Vorzeichen +NullByte
char buffer[BufferSize];


template<typename T> char* bigToStr(char* buffer, T value, uint8_t base = 10)
{
  bool negative = value < 0;
  if(negative) value *= -1LL;

  char* ptr = buffer+BufferSize;
  *(--ptr)  = 0;
  switch(base)
  {

    case  2: while(value) // BINARY
             {
               *(--ptr)  = '0' + (value & 0x01);
               value >>= 1;
             }
             break;
             
    case  8: while(value) // OKTAL
             {
               *(--ptr)  = '0' + (value & 0x07);
               value >>= 3;
             }
             break;
             
    case 16: while(value) // HEX
             {
               *(--ptr)  = '0' + (value & 0x0f);
               if(*ptr > '9') *ptr += 'A'-'9'-1;
               value >>= 4;
             }
             break;
           
    default: while(value) // UNIVERSAL
             {
               uint8_t rest = value % base;
               *(--ptr)  = '0' + rest;
               if(*ptr > '9') *ptr += 'A'-'9'-1;
               value /= base;
             }
             break;
  }
  if(negative)  *(--ptr)  = '-';
  return ptr;
}



void setup()
{
  Serial.begin(9600);
  Serial.println("Start");
  for(int i=0;i<8;i++)Serial.print("0123456789");
  Serial.println("");
 
 Serial.println(bigToStr(buffer,test, 2));
 Serial.println(bigToStr(buffer,test, 8));
 Serial.println(bigToStr(buffer,test,16));
 Serial.println(bigToStr(buffer,144,12));
 Serial.println(bigToStr(buffer,-144));
 Serial.println(bigToStr(buffer,1234567890123456789ULL));
 Serial.println(bigToStr(buffer,-1234567890123456789LL));

}

void loop()
{

}

Funktioniert das bei dir?
Kluge Worte zu schreiben, ist schwer.
Schon ein einziger Buchstabendreher, kann alles urinieren.

agmue

#3
Aug 13, 2018, 07:03 am Last Edit: Aug 13, 2018, 07:09 am by agmue
2. Die betreffende platform.txt anpassen
Habe ich schon mal geändert:

compiler.cpp.flags=-c -g -Os {compiler.warning_flags} -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -MMD -std=gnu++11

Wie sähe die neue Änderung aus?

Funktioniert das bei dir?
Keine Fehler, keine Warnungen vom Kompiler, Ausgabe:

Start
01234567890123456789012345678901234567890123456789012345678901234567890123456789
1111111111111111111111111111111111111111111111111111111111111111
1777777777777777777777
FFFFFFFFFFFFFFFF
100
-144
1234567890123456789
-1234567890123456789


Danke!

combie

#4
Aug 13, 2018, 07:45 am Last Edit: Aug 14, 2018, 11:48 am by combie
Ja, die Ausgabe sieht doch gut aus!
Die Templateversion funktioniert also.

Bei mir ( 1.8.5 ) sieht die originale Zeile in der platform.txt so aus:
Code: [Select]
compiler.cpp.flags=-c -g -Os {compiler.warning_flags} -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -MMD -flto

Sollte also bei dir auch richtig sein.(warum das dann nicht mit der auto Variante funktioniert, ka)
Die  -flto (LinkTimeOptimization) ist neu in 1.8.5

Der ESP xtensa Compiler kann das auto in der Parameterliste auch (noch) nicht.
Obwohl dort C++11 eingestellt ist.

Herzlichen Dank, für den Test.
So, als Template, ist es breiter einsetzbar.




Kluge Worte zu schreiben, ist schwer.
Schon ein einziger Buchstabendreher, kann alles urinieren.

ArduFE

Der ESP xtensa Compiler kann das auto in der Parameterliste auch (noch) nicht.
Obwohl dort C++11 eingestellt ist.
Das auto an dieser Stelle ist eine GNU-Erweiterung, sie sollte auch nur bei -std=gnu++... funktionieren, nicht bei -std=c++... .

Normales C++ erlaubt auto nur in der Parameterliste von lambda Funktionen (generic lambda seit C++14).

Technisch ist es aber nur eine Kurzschreibweise der template Version.

combie

Gut!
Dann ist das auch geklärt, und es bleibt bei der Template Version!
Danke.
Kluge Worte zu schreiben, ist schwer.
Schon ein einziger Buchstabendreher, kann alles urinieren.

agmue

Herzlichen Dank, für den Test.
Bitte gerne, war mir ein Vergnügen :)

combie

#8
Aug 13, 2018, 10:38 am Last Edit: Aug 13, 2018, 02:08 pm by combie
Noch mehr Lust, zu testen...?  :o

Denn, was hilft die schönste Ausgabe, wenn man keine solchen Zahlen einlesen kann?

Also hier noch ein Parser um Zahlen zu (fast) jeder beliebigen Base einlesen zu können.

Funktionssignatur:
>  char* strToBig(char* inStr, T & outVar, uint8_t base = 10)

"inStr" ist ein Zeiger auf die auszuwertende Zeichenkette
Vorlaufendes - wird verarbeitet
Vorlaufendes + wird ignoriert
Es wird bis zum ersten Zeichen gelesen, welches sich nicht in dem Zahlensystem abbilden lässt.

"outVar" ist die Variable, in der das Ergebnis des parsens landet.
Diese Variable darf von einem beliebigen integer Datentype sein, bis incl 64Bit.
Auch Vorzeichenbehaftet.

"base" ist die Zahlenbasis, die Grundlage der Wandlung.
Base muss >=2 sein.
Base darf so groß werden, wie es der Zeichenvorrat zulässt.

Rückgabe:
Ein Zeiger in den "inStr", auf das erste, nicht auswertbare, Zeichen.


Zum goto:
Dieses war die erste Variante, welche mir in den Sinn kam.
Und sie hat sofort den Test bestanden. Ohne jede Korrektur.

Wenn jemand eine Idee hat, wie man das ohne goto, noch schöner, hin bekommt, dann gerne!


Code: [Select]


template<typename T> char* strToBig(char* inStr, T & outVar, uint8_t base = 10)
{
 
  // Start:
    outVar         = 0;
    char *ptr      = inStr;
    uint8_t symbol = *ptr;
    bool negative  = symbol == '-';
    if(symbol == '+') goto Next; // + ignorieren
    if(!negative)     goto Scan;

  Next:
    symbol = *(++ptr); // Zeiger weiter setzen, Symbol aus dem String lesen
   
  Scan:  // Symbol zur Zahl wandeln
    if(symbol < '0') goto Exit;
    if(symbol > '9' && symbol < 'A') goto Exit;
    if(symbol >= 'A') symbol -= 'A' - '9' - 1;
    symbol -= '0';
    if(symbol >= base) goto Exit;
    outVar *= base;
    outVar += symbol;
    goto Next;

  Exit: 
    if(negative) outVar *= -1LL;
    return ptr;
}


// ----------


template<typename T> char* bigToStr(char* buffer, T value, uint8_t base = 10)
{
  bool negative = value < 0;
  if(negative) value *= -1LL;

  //char* ptr = buffer+BufferSize;
  char* ptr = buffer+sizeof(value)*8+1+1; // buffergroesse berechnen, size * 8bit +Vorzeichen +NullByte
  *(--ptr)  = 0;
  switch(base)
  {

    case  2: while(value) // BINARY
             {
               *(--ptr)  = '0' + (value & 0x01);
               value >>= 1;
             }
             break;
             
    case  8: while(value) // OKTAL
             {
               *(--ptr)  = '0' + (value & 0x07);
               value >>= 3;
             }
             break;
             
    case 16: while(value) // HEX
             {
               *(--ptr)  = '0' + (value & 0x0f);
               if(*ptr > '9') *ptr += 'A'-'9'-1;
               value >>= 4;
             }
             break;
           
    default: while(value) // UNIVERSAL
             {
               uint8_t rest = value % base;
               *(--ptr)  = '0' + rest;
               if(*ptr > '9') *ptr += 'A'-'9'-1;
               value /= base;
             }
             break;
  }
  if(negative)  *(--ptr)  = '-';
  return ptr;
}



// ----------



void setup()
{
  Serial.begin(9600);
  Serial.println("Start");

 

  int var;
  char strA[] = "42Zeichensalat";
  strToBig(strA, var);
  Serial.println(var);
 
  char strB[] = "-42mulm";
  strToBig(strB, var);
  Serial.println(var);
 
  char strC[] = "FF Flaschen";
  strToBig(strC, var,16);
  Serial.println(var);
 
  char strD[] = "10101010";
  strToBig(strD, var,2);
  Serial.println(var,HEX);
 
  Serial.println("---");


  size_t geleseneZeichen;
  char buffer[100];
  unsigned long long varB;
  char zeichenkette[] = "1777777777777777777777"; // oktal
  geleseneZeichen = strToBig(zeichenkette, varB,8) - zeichenkette;
  Serial.print("Es wurden ");Serial.print(geleseneZeichen);Serial.println(" Zeichen ausgewertet");
  Serial.println(bigToStr(buffer,varB));
  Serial.println(bigToStr(buffer,varB,16));
}

void loop()
{

}


Kluge Worte zu schreiben, ist schwer.
Schon ein einziger Buchstabendreher, kann alles urinieren.

agmue

#9
Aug 13, 2018, 03:16 pm Last Edit: Aug 13, 2018, 03:24 pm by agmue
Wenn jemand eine Idee hat, wie man das ohne goto, noch schöner, hin bekommt, dann gerne!
Ich vermisse goto, denn damit konnte man schön unübersichtliche Programme schreiben und sich etwas wie Leonardo fühlen.

Ob schöner, weiß ich nicht, aber ohne goto:

Code: [Select]
template<typename T> char* strToBig(char* inStr, T & outVar, uint8_t base = 10)
{
  outVar         = 0;
  char *ptr      = inStr;
  uint8_t symbol = *ptr;
  bool negative  = symbol == '-';
  if (symbol == '+' || symbol == '-') symbol = *(++ptr); // + ignorieren, Zeiger weiter setzen, Symbol aus dem String lesen
  while (1) {
    if ((symbol >= '0' && symbol <= '9') || (symbol >= 'A' && symbol <= 'F')) {
      symbol -= '0';
      if (symbol > 9) symbol -= 7;
      if (symbol < base) {
        outVar *= base;
        outVar += symbol;
        symbol = *(++ptr); // Zeiger weiter setzen, Symbol aus dem String lesen
      }
    } else {
      if (negative) outVar *= -1LL;
      return ptr;
    }
  }
}

Ein return in while ist auch wie goto, aber nunja, auf einen Merker hatte ich keine Lust.

combie

#10
Aug 13, 2018, 05:41 pm Last Edit: Aug 13, 2018, 07:48 pm by combie
Ja, fein, so gehts ohne.
Leider, erkauft durch eine drei mal tiefere Schachtelung.

Naja..
So kann dann jeder seiner Vorliebe frönen.
Mir soll das alles recht sein.
---

Aber das hier " && symbol <= 'F'" bewirkt eine Begrenzung auf base <= 16

z.B. base = 20 ist dann nicht mehr möglich.
Der Zeichenvorrat wird nicht vollständig ausgenutzt.

Damit ist eine der beiden Fliegen, tot, welche ich eigentlich jagen wollte.
Eine beliebige Zahlenbasis und flexible Datentypen.
Kluge Worte zu schreiben, ist schwer.
Schon ein einziger Buchstabendreher, kann alles urinieren.

agmue

Aber das hier " && symbol <= 'F'" bewirkt eine Begrenzung auf base <= 16
Wie wäre es dann mit " && symbol <= 'Z'"?

Da fallen mir auch noch die kleinen Buchstaben ein, was soll damit passieren?

Go Up