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


von Erik H. (agutanus)


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...

von Erik H. (agutanus)


Lesenswert?

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

ist wohl nicht sonderlich effizient, aber funktioniert.

von Rolf Magnus (Gast)


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.

von Falk B. (falk)


Lesenswert?

@  Erik Her (agutanus)

Hier etwas effizienter und aufgeräumt.
1
//wie "utoa(byte, buffer, 2)" - ABER gibt alle Nullen aus
2
void my_btoa(uint8_t byte, char* buffer)
3
{
4
  uint8_t i;            //Zähler (Schleife)
5
6
  for(i=0; i<8; i++)
7
  {
8
    if(byte & 0x80)      //Bitvergleich
9
      buffer[i] = '1';  
10
    else
11
      buffer[i] = '0';
12
    byte <<= 1;         //nächstes Bit vergleichen
13
  }
14
  buffer[8] = 0;        //String-Terminierung
15
}

MfG
Falk

von Erik H. (agutanus)


Lesenswert?

Rolf Magnus schrieb:
> Ich sehe jetzt nichts ineffizientes daran.

ich denke das
1
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'
__________________________________
1
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)

von Frank B. (f-baer)


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.

von Karl H. (kbuchegg)


Lesenswert?

Erik Her schrieb:

> ich denke das
>
1
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.

von Falk B. (falk)


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.

1
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
1
  a <<= 1;     // kurz
2
  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

von Rolf Magnus (Gast)


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 ;-)

von Erik H. (agutanus)


Lesenswert?

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

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.

von Karl H. (kbuchegg)


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.

von Karl H. (kbuchegg)


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.

von Karl H. (kbuchegg)


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.

von Falk B. (falk)


Lesenswert?

@  Erik Her (agutanus)

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

Besser so

  int8_t i;          //Zähler (Schleife)

MfG
Falk

von Erik H. (agutanus)


Lesenswert?

Erneut umgeschrieben:
1
void my_btoa(uint8_t byte, char* buffer)
2
{
3
  uint8_t i = 8;          //Zähler (Schleife)
4
5
  do {
6
    i --;
7
    buffer[i] = (byte & 0x01) + '0';
8
    byte >>= 1;        //nächstes Bit vergleichen
9
  } while(i > 0);
10
11
  buffer[8] = 0;        //String-Terminierung
12
}

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:
1
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:?
1
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)

von Erik H. (agutanus)


Lesenswert?

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

von Falk B. (falk)


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

von Karl H. (kbuchegg)


Lesenswert?

Erik Her schrieb:

> Zum Register beschreiben wird doch meist:
>
1
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:?
>
1
REGISTER = 0b00010000   //Beispiel

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

von Erik H. (agutanus)


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...

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.