Hallo,
Ich habe ein Array mit 8 Werten, z.B.:
BYTE value[8] = { 2, 5, 0, 0, 1, 7, 3, 0 };
Aus diesem Array möchte ich nun einen Byte-Vektor generieren, für
welchen gilt:
- Bit0 ist 0 wenn value[0] = 0, sonst 1
- ...
- Bit7 ist 0 wenn value[7] = 0, sonst 1
Ich möchte also im Vektor anzeigen, ob die Array-Elemente 0 sind oder
nicht.
Wie codiere ich das am effizientesten in C?
Das könnte funktionieren (ohne Gewähr und ungetestet): 1 | uint8_t vector = 0;
| 2 | for(int8_t i=7; i>=0; i++)
| 3 | {
| 4 | vector |= value[i] && 1;
| 5 | vector <<= 1;
| 6 | }
|
In "vector" sollte dann das stehen, was Du oben haben wolltest.
Johannes M. wrote:
> 1 | > vector |= value[i] && 1;
| 2 | >
|
Nein, das stimmt nicht. Das prüft nur das LSB in value[x].
Es müsste
1 | vector|= (value[i]?1:0);
|
heissen. Auch sollte zuerst geshifted werden, sonst ist das vector-LSB
immer low....
Niels Hüsken wrote:
> Johannes M. wrote:
>> 1 | >> vector |= value[i] && 1;
| 2 | >>
|
>
> Nein, das stimmt nicht. Das prüft nur das LSB in value[x].
> Es müsste
Nein, das stimmt nicht. Es überprüft ob i!=0 (Also logisch wahr ist).
Falls ja, evaluiert der Ausdruck zu "1" ansonsten zu "0".
> 1 | > vector|= (value[i]?1:0);
| 2 | >
|
Das macht das Gleiche (oder das Selbe? ;))
Simon K. wrote:
> Falls ja, evaluiert der Ausdruck zu "1" ansonsten zu "0".
Der Ausdruck evaluiert zu nicht 0, richtig?
Obs eine 1 oder sonst was wird, ist doch compilerabhängig?
Simon K. wrote:
> Nein, das stimmt nicht. Es überprüft ob i!=0 (Also logisch wahr ist).
> Falls ja, evaluiert der Ausdruck zu "1" ansonsten zu "0".
Genau so war es gedacht...
>> 1 | >> vector|= (value[i]?1:0);
| 2 | >>
|
>
> Das macht das Gleiche (oder das Selbe? ;))
Im Prinzip ja. Und was der Compiler draus macht, dürfte auch ungefähr
auf das selbe hinauslaufen...
Man muss nur dafür sorgen (wie auch immer), dass aus dem Wert in
value[i] ein Wahrheitswert wird. Und der kann nunmal nur 0 oder 1 sein.
@Niels:
Du verwechselst "&&" mit "&"! Das was Du meinst, ist "&", das steht aber
nicht da...
Martin Kohler wrote:
> Der Ausdruck evaluiert zu nicht 0, richtig?
Und "nicht 0" ist in C standardmäßig "1" (als Ausgabewert).
> Obs eine 1 oder sonst was wird, ist doch compilerabhängig?
Nö. ANSI sagt, dass das Ergebnis einer logischen Verknüpfung 0 oder 1
ist. Als *Eingabe*-Wert stimmt Deine Annahme, dass alles, was nicht 0
ist, als "true" interpretiert wird. Aber auch nur da.
Rein theoretisch müsste auch 1 | vector |= !(!(value[i]));
|
Gehen. Es gibt bestimmt was kürzeres, aber da müsste dann mal einer der
Profis ran ;)
C ist jetzt nicht so meine Domäne, aber:
1 | uint8_t vector = 0;
| 2 | for(int8_t i=7; i>=0; i++)
| 3 | {
| 4 | vector |= value[i] && 1;
| 5 | vector <<= 1;
| 6 | }
|
bewirkt, dass das Endergebnis eins zu weit nach links geshiftet wird.
&& ist doch nur für booleans oder? Wenn C eh implizite Typecasts zulässt
sollte Folgendes gehen:
1 | uint8_t vector = 0;
| 2 | for(int8_t i = 7; i >= 0; i++)
| 3 | {
| 4 | vector = (value[i] << 1) | (value[i] != 0);
| 5 | }
|
oder?
Simon K. wrote:
> Nein, das stimmt nicht. Es überprüft ob i!=0 (Also logisch wahr ist).
F*ck! Sorry..hab das doppelte "&&" überlesen...mir war, da wäre nur ein
einfaches "&". Naja, wer Augen hat wie ein Maulwurf.....
> Falls ja, evaluiert der Ausdruck zu "1" ansonsten zu "0".
Das Logisch True "1" ergibt, ist Compiler/ Platform abhängig. Wenn der
AVR-GCC das so macht, ist das OK, ist aber nicht standardisiert, soweit
ich weiss.
IMO wäre die x?1:0 Lösung vorzuziehen.
Kai Giebeler wrote:
> C ist jetzt nicht so meine Domäne, aber:
> [...]
> bewirkt, dass das Endergebnis eins zu weit nach links geshiftet wird.
Stimmt. Müsste sich vermeiden lassen, indem man die beiden Zeilen
vertauscht. Beim ersten mal ist es egal, dass einmal geschoben wird,
steht eh ne Null drin...
> && ist doch nur für booleans oder? Wenn C eh implizite Typecasts zulässt
> sollte Folgendes gehen:
>
> [...]
>
> oder?
Nein, das geht nicht. value[i] ist kein Wahrheitswert. Genau deshalb
muss ja erst mit irgendeiner logischen Verknüpfung einer draus gemacht
werden. Und "&& 1" macht genau das. "|" ist hingegen wieder kein
logischer, sondern ein bitweiser Operator. Das haut nicht hin.
Interessanterweise hat noch keiner bemängelt, dass es "i--" anstelle
"i++" heissen muss ;-)
Ich fasse dann mal zusammen: 1 | uint8_t vector = 0;
| 2 | for(int8_t i=7; i>=0; i--)
| 3 | {
| 4 | vector <<= 1;
| 5 | vector |= value[i] ? 1 : 0;
| 6 | }
|
Das Zielsystem ist übrigens ein Cypress Controller, mit KEIL
programmiert.
Oder ganz sicher und für jeden verständlich:
1 | uint8_t vector;
| 2 | for(uint8_t i=0; i<8; i++)
| 3 | {
| 4 | vector >>= 1;
| 5 | if (value[i])
| 6 | vector |= 128;
| 7 | }
|
Martin Kohler wrote:
> Interessanterweise hat noch keiner bemängelt, dass es "i--" anstelle
> "i++" heissen muss ;-)
Schön, dass Du das selber rausgefunden hast. Wie sagte mein Mathe-Lehrer
immer, wenn er was falsches an die Tafel geschrieben hatte und ein
Schüler ihn drauf aufmerksam machte: "Wollte nur sehen, ob Sie auch
aufpassen..."
Martin Kohler wrote:
> Interessanterweise hat noch keiner bemängelt, dass es "i--" anstelle
> "i++" heissen muss ;-)
Nein, muss es nicht, aber da ist trozdem ein fehler. Intention war es
wohl, i von 7 bis 0 zählen zu lassen. Da UINT8_T niemals <0 werden kann,
haben wir eine endloschleife....
Insofern hast du schon recht....
Niels Hüsken wrote:
> Das Logisch True "1" ergibt, ist Compiler/ Platform abhängig. Wenn der
> AVR-GCC das so macht, ist das OK, ist aber nicht standardisiert, soweit
> ich weiss.
Doch, ist es. Wäre sonst ein wenig chaotisch, wenn alles Mögliche
rauskommen könnte.
Benedikt K. wrote:
> Oder ganz sicher und für jeden verständlich:
>
> 1 | > uint8_t vector;
| 2 | > for(uint8_t i=0; i<8; i++)
| 3 | > {
| 4 | > vector >>= 1;
| 5 | > if (value[i])
| 6 | > vector |= 128;
| 7 | > }
| 8 | >
|
Ja, diese Version würde ich auch vorziehen. Obige ist zwar schön
geschrieben, aber für Anfänger bspw. unverständlich, was da jetzt ein
Logischer Operator soll. Der Code dürfte aber in beiden Fällen gleich
sein.
Uiuiui... gleich mehrere sich überschneidende Beiträge...!
Die Variante von Benedikt K. ist m.E. die einfachste und auch lesbarste
--> wird so gewählt.
Danke für die Antworten!
Niels Hüsken wrote:
> Nein, muss es nicht, aber da ist trozdem ein fehler. Intention war es
> wohl, i von 7 bis 0 zählen zu lassen. Da UINT8_T niemals <0 werden kann,
> haben wir eine endloschleife....
Doch, muss es. Genau deshalb steht da auch kein uint8_t, sondern ein
int8_t, und der kann kleiner Null werden! War ein Tippfehler, weil ich
das ganze andersrum angefangen habe (so, wie Benedikt es gemacht hat),
und danach beim Ändern vergessen hab, aus dem "++" ein "--" zu machen...
>IMO wäre die x?1:0 Lösung vorzuziehen.
Ist auf jeden Fall die sauberste Lösung, wenn die casts nicht sauber
definiert sind.
Unter folgendem Aspekt
>Wie codiere ich das am effizientesten in C?
muss der Autor zwischen Geschwindigkeit und Portabilität abwägen. Der
ternäre Operator ist wahrscheinlich nicht so gut zu optimieren wie ein
type cast.
Bin jetzt einfach mal von Effizienz = Geschwindigkeit ausgegangen...
>> && ist doch nur für booleans oder? Wenn C eh implizite Typecasts zulässt
>> sollte Folgendes gehen:
>>
>> [...]
>>
>> oder?
>Nein, das geht nicht. value[i] ist kein Wahrheitswert. Genau deshalb
>muss ja erst mit irgendeiner logischen Verknüpfung einer draus gemacht
Deshalb mache ich ja auch einen Integer-Vergleich um die Konversion
vorzunehmen.
>werden. Und "&& 1" macht genau das. "|" ist hingegen wieder kein
>logischer, sondern ein bitweiser Operator. Das haut nicht hin.
Beim bitweisen Operator verlasse ich mich auf den impliziten Cast zum
Integer (wie ihr oben auch mit dem Operator |= ). Sollte in meinen Augen
also gut funktionieren.
Johannes M. wrote:
>> Das Logisch True "1" ergibt, ist Compiler/ Platform abhängig. Wenn der
>> AVR-GCC das so macht, ist das OK, ist aber nicht standardisiert, soweit
>> ich weiss.
> Doch, ist es. Wäre sonst ein wenig chaotisch, wenn alles Mögliche
> rauskommen könnte.
Nein, alles was nicht 0 ist, ist logisch "true". Und nein, es ist nicht
Standardisiert. Auch da bin ich mir ziemlich sicher, weil ich das Thema
schon öfter hatte.
Ich weiss z.B. das der Borland-C Compiler, mit dem ich mich beschäftigt
habe, True immer als "-1" evaluiert hat....
@Kai:
> Deshalb mache ich ja auch einen Integer-Vergleich um die Konversion
> vorzunehmen.
Hast recht. Ist im Prinzip genau das, was ich geschrieben hatte, nur
eben in einer Zeile. Kam mir nur zuerst ein bisschen komisch vor...
Niels Hüsken wrote:
> Nein, alles was nicht 0 ist, ist logisch "true". Und nein, es ist nicht
> Standardisiert. Auch da bin ich mir ziemlich sicher, weil ich das Thema
> schon öfter hatte.
Dass alles, was nicht Null ist, true ist, ist mir klar, aber i.m.E. nur
als Eingabewert. Dass das Ergebnis einer logischen Operation ebenfalls
alles sein kann, wäre mir neu (und ich meine auch, mich erinnern zu
können, dass wir das schon öfter hier hatten). Aber das dürfte sich
überprüfen lassen.
Bei diesem Code-Segment würde ich ja jetzt erwarten, dass am Schluss
0x04 im Vektor steht: 1 | char value[8] = { 0, 0, 1, 0, 0, 0, 0, 0 };
| 2 | char vector = 0;
| 3 | count=0;
| 4 |
| 5 | for(i=0; i<8; i++)
| 6 | {
| 7 | vector >>= 1;
| 8 | if (value[i])
| 9 | vector |= 128;
| 10 | }
|
Aber was macht VisualStudio daraus? (jaaa, wer arbeitet schon damit...
nur zu Testzwecken!)
Resultat: 0xFC !!!!
Sobald eine 1 im MSB steht, werden nur noch 1 nachgeschoben, keine 0
mehr?!?!
Ist das ein Bug oder ein Feature?
hehe ... die Schiebeoperation kopiert das oberste Bit. Einfach vector
auf 16 bits Breite deklarieren, dann geht's.
Johannes M. wrote:
> Aber das dürfte sich überprüfen lassen.
Ja, das findest du irgendwo in den Dokumenten zum ISO/IEC 9899:1999
Standard. Aber ich habs gerade mal überprüft. Der AVR-GCC evaluiert
"True" auf jedenfall zu "1", also funktioniert value[x]&&1 auch
richtig...
Kai Giebeler wrote:
> hehe ... die Schiebeoperation kopiert das oberste Bit.
Sicher? 1 | char value[8] = { 0, 0, 1, 0, 0, 0, 0, 0 };
| 2 | char vector = 0;
| 3 | count=0;
| 4 |
| 5 | for(i=8; i>0; i++)
| 6 | {
| 7 | vector <<= 1;
| 8 | if (value[i])
| 9 | vector |= 0x01;
| 10 | }
| 11 |
| 12 | count <<=1;
| 13 | count <<=1;
| 14 |
| 15 | count++;
| 16 |
| 17 | count <<=1;
| 18 | count <<=1;
|
Warum kommt dann hier vector=0xfe und count=0x04 raus??
Bei vector wird das unterste Bit kopiert (diesmal shift-right!), bei
count jedoch nicht ???
@Martin:
Vorsicht! Keine Schiebeoperationen mit vorzeichenbehafteten Werten
machen! Da kann nämlich tatsächlich alles Mögliche rauskommen. Ich hab
schon mit Absicht einen uint8_t für vector genommen! Mach es wenn
überhaupt dann mit einem unsigned char.
Martin Kohler wrote:
> Kai Giebeler wrote:
>> hehe ... die Schiebeoperation kopiert das oberste Bit.
>
> Sicher?
Nein.
Es hängt davon ab, ob für den Compiler ein plain vanilla 'char'
ein 'signed char' oder ein 'unsigned char' ist.
Du willst in diesem Fall auf jeden Fall, dass sich der
Compiler aus allem raushält und die Bits so nimmt wie
sie sind. Daher solltest du in diesem Fall explizit
sein und es nicht dem Compiler überlassen, was er nimmt.
Du willst unsigned char
So, ich hab grad mal im K&R (2. Auflage, ANSI C) nachgesehen und wurde
zumindest von dieser Quelle bestätigt:
Zitat:
"By definition, the numeric value of a relational or logical expression
is 1 if the relation is true, and 0 if the relation is false.
The unary negation operator ! converts a non-zero operand into 0, and a
zero operand into 1."
Wenn der Borland-Compiler etwas anderes fabriziert, ist er entweder kein
ANSI-C-Compiler oder der K&R lügt...
Kai Giebeler wrote:
> hehe ... die Schiebeoperation kopiert das oberste Bit. Einfach vector
> auf 16 bits Breite deklarieren, dann geht's.
Nachtrag: Die Schiebeoperation kopiert nur beim Datentyp "char" das
oberste Bit, d.h. bei einer negativen Zahl werden 1 reingeschoben, bei
einer positiven 0.
Bei unsigned char werden immer 0 reingeschoben.
Martin Kohler wrote:
> Nachtrag: Die Schiebeoperation kopiert nur beim Datentyp "char" das
> oberste Bit, d.h. bei einer negativen Zahl werden 1 reingeschoben, bei
> einer positiven 0.
Vorsicht mit dieser Aussage.
Ob char als 'signed char' oder als 'unsigned char' angesehen wird,
entscheidet der Compilerbauer. Der C-Standard lässt ihm diese
Freiheit.
Man kann also nicht generell sage, dass dieses Verhalten beim
Datentyp 'char' auftritt. Wenn der Compilerbauer eine 'char'
als 'unsigned char' aufgefasst hat, dann stimmt die Aussage
so nicht.
Viele Compiler haben auch Command Line switches mit der sich
die Vorgabe des Compilerbauers überschreiben lässt.
Karl heinz Buchegger wrote:
> Vorsicht mit dieser Aussage.
> Ob char als 'signed char' oder als 'unsigned char' angesehen wird,
> entscheidet der Compilerbauer. Der C-Standard lässt ihm diese
> Freiheit.
Präzisierung:
hier kommt 0x10 raus: 1 | {
| 2 | unsigned char count = 0x80;
| 3 | count >>=1;
| 4 | count >>=1;
| 5 | count >>=1;
| 6 | }
|
und hier kommt 0xf0 raus: 1 | {
| 2 | signed char count = 0x80;
| 3 | count >>=1;
| 4 | count >>=1;
| 5 | count >>=1;
| 6 | }
|
Wo finde ich die Angabe des ANSI-Standards, dass bei signed char bei
shift-right das MSB kopiert wird?
Martin Kohler wrote:
> Wo finde ich die Angabe des ANSI-Standards, dass bei signed char bei
> shift-right das MSB kopiert wird?
Im Grunde nirgends, das folgt aus der Verwendung des 2-er Komplements
für Arithmetik. Welches Verhalten >> und << bei signed Datentypen
haben ist meines Wissens so nicht eindeutig geregelt (genauso
wie beispielsweise das Overflow/Underflow Verhalten bei signed
Datentypen nicht geregelt ist). Da man aber Konsistenz haben will,
wird man das bei Verwendung von 2-er Komplement Arithmetik so machen,
damit der Zusammenhang zwischen Schiebeoperatio und Multiplikation
bzw. Division erhalten bleibt.
Aber auch hier Achtung: Im Grunde ist auch nirgends festgeschrieben,
dass 2-er Komplement verwendet werden muss. Kann mich aber nicht daran
erinnern je von einem Compiler gehört zu haben, der das nicht tut.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
|