www.mikrocontroller.net

Forum: Compiler & IDEs mit utoa binär ausgeben, aber mit Anführungsnullen


Autor: Erik H. (agutanus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,
ich habe leider nichts vernünftiges im Forum oder Google gefunden...
Daher die Frage:
kann ich über die Funktion utoa auch strings mit Anführungsnullen 
erzeugen?

Ich möchte Werte binär ausgeben, leider schneidet utoa immer die Nullen 
am Anfang ab, so dass statt 00010110 nur 10110 ausgegeben wird...

gibts da irgend eine Möglichkeit?

Ansonsten müsste ich mir ne eigene Funktion schreiben...

Autor: Erik H. (agutanus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ach... hab mir jetzt eine selber geschrieben:
//wie "utoa(byte, buffer, 2)" - ABER gibt alle Nullen aus
void my_btoa(uint8_t byte, char* buffer)
{
  uint8_t i = 0;        //Zähler (Schleife)
  uint8_t j = 128;      //Wert für Bitvergleich
  for(i=0; i<8; i++)
  {
    if(byte & j)      //Bitvergleich
      buffer[i] = 49;  //wenn <> 0, dann char = 49 = '1'
    else
      buffer[i] = 48;  //sonst char = 48 = '0'
    j /= 2;          //nächstes Bit vergleichen
  }
  buffer[8] = 0;        //String-Terminierung
}

ist wohl nicht sonderlich effizient, aber funktioniert.

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Erik Her schrieb:
>       buffer[i] = 49;  //wenn <> 0, dann char = 49 = '1'

Warum weist du 49 zu und erklärst dann in einem Kommentar, daß damit '1' 
gemeint ist, statt einfach gleich '1' zuzuweisen?

> ist wohl nicht sonderlich effizient, aber funktioniert.

Ich sehe jetzt nichts ineffizientes daran.

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@  Erik Her (agutanus)

Hier etwas effizienter und aufgeräumt.
//wie "utoa(byte, buffer, 2)" - ABER gibt alle Nullen aus
void my_btoa(uint8_t byte, char* buffer)
{
  uint8_t i;            //Zähler (Schleife)

  for(i=0; i<8; i++)
  {
    if(byte & 0x80)      //Bitvergleich
      buffer[i] = '1';  
    else
      buffer[i] = '0';
    byte <<= 1;         //nächstes Bit vergleichen
  }
  buffer[8] = 0;        //String-Terminierung
}

MfG
Falk

Autor: Erik H. (agutanus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Rolf Magnus schrieb:
> Ich sehe jetzt nichts ineffizientes daran.

ich denke das
j /= 2;   //nächstes Bit vergleichen
braucht unnötig viele Taktzyklen...
(bin mir nicht sicher, in wie viele Schritte die Division kompiliert 
wird)

Außerdem hatte ich gehofft das ganze ohne if-Abfrage zu gestalten:
nach dem Bit-Vergleich eine 1 oder 0 zu erhalten und dann 48 dazu 
addieren. Das ergibt dann eine '1' oder '0'
__________________________________
byte <<= 1;    //nächstes Bit vergleichen
diesen "<<=" Operator habe ich noch nie gesehen und google findet ihn 
auch nicht... Was macht der?

Läuft auch damit!
Die Änderung spart am Ende 6 Byte Programmspeicher... (zuzüglich 1 Byte 
SRAM)

Autor: Frank Bär (f-baer)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Erik Her schrieb:
> diesen "<<=" Operator habe ich noch nie gesehen und google findet ihn
> auch nicht... Was macht der?

Das ist ein Bitshift-Operator.

z << a verschiebt um a Stellen nach links, >> dementsprechend nach 
rechts.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Erik Her schrieb:

> ich denke das
>
j /= 2;   //nächstes Bit vergleichen
> braucht unnötig viele Taktzyklen...
> (bin mir nicht sicher, in wie viele Schritte die Division kompiliert
> wird)

Da j ein uint8_t ist, wird das in genau einen Schiebebefehl übersetzt.

Hört endlich auf mit diesem Unsinn, Multiplikationen und Divisionen mit 
2-er Potenzen selbst dursch Shift zu ersetzten! Compiler machen diese 
Ersetzung seit mehr als 40 Jahren völlig autonom! Compilerbauer sind 
doch keine Trottel.

> Außerdem hatte ich gehofft das ganze ohne if-Abfrage zu gestalten:
> nach dem Bit-Vergleich eine 1 oder 0 zu erhalten und dann 48 dazu
> addieren. Das ergibt dann eine '1' oder '0'

Das kannst du im Prinzip machen, aber effizienter wird es dadurch 
wahrscheinlich auch nicht. Denn zunächst muss ja das Ergebnis des 
Vergleichs von 0/nicht_0 auf 0/1 normiert werden. Daher gewinnst du 
nichts. Die C Schreibweise ist ein wenig kürzer, aber das bedeutet nicht 
viel.

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@  Karl heinz Buchegger (kbuchegg) (Moderator)

>Da j ein uint8_t ist, wird das in genau einen Schiebebefehl übersetzt.

Kann sein, muss nicht.

>Hört endlich auf mit diesem Unsinn, Multiplikationen und Divisionen mit
>2-er Potenzen selbst dursch Shift zu ersetzten!

Erstens ist die Schiebeoperation hier DEUTLICH logischer, man versteht 
worum es geht.

> Compiler machen diese
>Ersetzung seit mehr als 40 Jahren völlig autonom! Compilerbauer sind
>doch keine Trottel.

Zweitens ist das schlicht so allgemein nicht wahr. Hab ich letztens auf 
dem MPLAB C-Compiler von Microchip gesehen, eine Schiebeoperation war 
deutlich schneller als eine Divison mit der entsprechenden Zahl. Und es 
waren KONSTANTEN!!

@  Erik Her (agutanus)

>Außerdem hatte ich gehofft das ganze ohne if-Abfrage zu gestalten:
>nach dem Bit-Vergleich eine 1 oder 0 zu erhalten und dann 48 dazu
>addieren. Das ergibt dann eine '1' oder '0'

Das geht nur wenn du beim LSB anfängst.

buffer[i] = (byte & 0x01) +'0';

So funktioniert das normale itoa. Siehe Festkommaarithmetik.
Ist aber zumindest auf dem AVR nicht schneller, der kann einzelne Bits 
testen und verzweigen.

>diesen "<<=" Operator habe ich noch nie gesehen und google findet ihn
>auch nicht... Was macht der?

Schieben und zuweisen
  a <<= 1;     // kurz
  a   = a <<1; // lang, identisch

>Die Änderung spart am Ende 6 Byte Programmspeicher... (zuzüglich 1 Byte
>SRAM)

Die können manchmal entscheidend sein. In einem Hobbyprojekt hab ich mal 
einen ATmega8515 bis auf 4 (VIER) Bytes Flash voll ausgereizt. Das ging 
aber erst nach Optimierung und Mikrooptimierung des Codes.

MfG
Falk

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Karl heinz Buchegger schrieb:
> Da j ein uint8_t ist, wird das in genau einen Schiebebefehl übersetzt.
>
> Hört endlich auf mit diesem Unsinn, Multiplikationen und Divisionen mit
> 2-er Potenzen selbst dursch Shift zu ersetzten!

Ich weiß, daß da ein gewisser Reflex einsetzt, wenn jemand eine Division 
durch einen Shift ersetzt, aber manchmal paßt der Shift tatsächlich 
besser. Immerhin geht es in dem Code darum, schrittweise durch alle Bits 
eines Bytes zu gehen und jeweils zu schauen, ob es gesetzt ist, und 
dafür wird ein gesetztes Bit durch eine Variable geschoben. Da stellt 
sich dann die Frage, ob es sinnvoll ist, den Shift durch eine Division 
zu ersetzen, nur weil der Compiler dann eh wieder einen Shift daraus 
macht ;-)

Autor: Erik H. (agutanus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Habe die Funktion erneut umgeschrieben:
void my_btoa(uint8_t byte, char* buffer)
{
  uint8_t i;          //Zähler (Schleife)
  for(i=7; i>=0; i--)
  {
    buffer[i] = (byte & 0x01) + '0';
    byte >>= 1;        //nächstes Bit vergleichen
  }
  buffer[8] = 0;        //String-Terminierung
}

Allerdings scheint der Compiler damit Probleme zu haben...
Das Programm ist nun um 130 (!!!) Bytes kleiner, aber nicht mehr 
lauffähig.
anscheinend hängt es sich in der Funktion auf.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Erik Her schrieb:
> Habe die Funktion erneut umgeschrieben:

> void my_btoa(uint8_t byte, char* buffer)
> {
>   uint8_t i;          //Zähler (Schleife)
>   for(i=7; i>=0; i--)

Da i ein uint8_t ist, kann i per Definition immer nur >= 0 sein. Es ist 
schlicht und ergreifend nicht möglich, dass  i>=0  jemals falsch wird.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Falk Brunner schrieb:
>
> Zweitens ist das schlicht so allgemein nicht wahr. Hab ich letztens auf
> dem MPLAB C-Compiler von Microchip gesehen, eine Schiebeoperation war
> deutlich schneller als eine Divison mit der entsprechenden Zahl. Und es
> waren KONSTANTEN!!


Dann nimm einen vernünftigen Compiler.
Das ist doch lächerlich, dass wir alle paar Wochen wieder dieselbe 
Diskussion haben. Wenn der Micochip Compiler das nicht hinkriegt, dann 
hast du entweder signed statt unsigned Variablen oder der Compiler ist 
einfach nur schwach. Diese Optimierung ist von der allereinfachsten 
Sorte und es gibt keinen Grund warum ein Compiler das nicht können 
sollte (es sei denn die Compilerbauer sind komplett zugekifft).
Ein Anfänger soll sich in erster Linie auf sein Problem konzentrieren 
und nicht auf diese vermeintlich "cleveren" Optimierungen, die sich 
regelmässig als Zeitbomben entpuppen.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Rolf Magnus schrieb:
> Karl heinz Buchegger schrieb:
>> Da j ein uint8_t ist, wird das in genau einen Schiebebefehl übersetzt.
>>
>> Hört endlich auf mit diesem Unsinn, Multiplikationen und Divisionen mit
>> 2-er Potenzen selbst dursch Shift zu ersetzten!
>
> Ich weiß, daß da ein gewisser Reflex einsetzt, wenn jemand eine Division
> durch einen Shift ersetzt, aber manchmal paßt der Shift tatsächlich
> besser.

Richtig.
Allerdings war es hier die Sorge des TO, dass eine Division zu langsam 
wäre.

> sich dann die Frage, ob es sinnvoll ist, den Shift durch eine Division
> zu ersetzen, nur weil der Compiler dann eh wieder einen Shift daraus
> macht ;-)

Das ist sicher nicht sinnvoll.
Wie du schon sagtest: Sinnvoll ist es, sich im Kontext klar zu machen, 
was die Absicht im Code besser ausdrückt: Shift oder Division und das 
dann zu benutzen.
Aber diese "Division ist zu langsam - nimm doch Shift; das zeigt dann 
auch noch welch gewaltiger C-Hacker du bist", ich kanns schon nicht mehr 
hören.

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@  Erik Her (agutanus)

>  uint8_t i;          //Zähler (Schleife)

Besser so

  int8_t i;          //Zähler (Schleife)

MfG
Falk

Autor: Erik H. (agutanus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Erneut umgeschrieben:
void my_btoa(uint8_t byte, char* buffer)
{
  uint8_t i = 8;          //Zähler (Schleife)

  do {
    i --;
    buffer[i] = (byte & 0x01) + '0';
    byte >>= 1;        //nächstes Bit vergleichen
  } while(i > 0);

  buffer[8] = 0;        //String-Terminierung
}

Im Gegensatz zur vorherigen Funktion (mit der if()-Abfrage) wurden 
wieder 6 Byte gespart. (also bereits 12 Byte weniger als die 
Ursprüngliche Funktion)

Die Optimierungen sind zwar in diesem Fall sinnlos, da es lediglich ein 
Programm zum Testen einer TWI-Verbindung (und Ausgabe der empfangenen 
Bytes) ist, Aber mich interessiert, wie ich im Anwendungsfall bestimmte 
Operationen möglichst effizient lösen kann.

Um mal eine Meinung zur Diskussion abzugeben:
Ich finde die Schreibweise mit dem Shift-Operator deutlich sinnvoller, 
als die Division durch 2. (Selbst wenn es vom Compiler gleich übersetzt 
wird)

Dazu hätte ich eine Frage:
Zum Register beschreiben wird doch meist:
REGISTER |= (1<<BITX);   //Beispiel
verwendet.
Ergibt das nach dem Kompilieren nicht 2 Operationen?
(1) shift
(2) Register beschreiben

oder optimieren die Compiler das automatisch dazu:?
REGISTER = 0b00010000   //Beispiel

(Anmerkung: habe bisher kein Assembler programmiert - und daher auch 
keine Ahnung, wie bestimmte Befehle übersetzt werden.
Aber das folgt in den nächsten Monaten noch)

Autor: Erik H. (agutanus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
zu spät zum Bearbeiten...
Fehler - der Code mit den Registern sollte folgendermaßen lauten:
PORTD |= (PIN4);
wird zu
PORTD |= 0b00010000;
???

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Erik Her (agutanus)

>oder optimieren die Compiler das automatisch dazu:?

>REGISTER = 0b00010000   //Beispiel

Ja, denn das Schieben besteht komplett aus Konstanten, das macht der 
Compiler vorher.

MfG
Falk

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Erik Her schrieb:

> Zum Register beschreiben wird doch meist:
>
REGISTER |= (1<<BITX);   //Beispiel
> verwendet.
> Ergibt das nach dem Kompilieren nicht 2 Operationen?
> (1) shift
> (2) Register beschreiben
>
Der Shift wird optimiert, da ja alle Bestandteile konstant sind und 
damit kann der Compiler den Wert selber ausrechnen.

> oder optimieren die Compiler das automatisch dazu:?
>
REGISTER = 0b00010000   //Beispiel

Das ist aber eine andere Operation :-)
Oder hast du hier nur das |= vergessen?

Autor: Erik H. (agutanus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ich habe das | vergessen.
im folgenden Beitrag habe ich das (1<<PIN4) vergessen.
 -> ich konnte meine eigenen Beiträge nicht mehr bearbeiten, da bereits 
Antworten geschrieben wurden...

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]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [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.