Forum: Mikrocontroller und Digitale Elektronik Shift-Operation im Makro mit UND


von Shift (Gast)


Lesenswert?

Hallo,

ich sehe hier in einem Programm folgende eine Konstruktion, warum macht 
man das so?


#define MAKRO 3
#define NEUESMAKRO ((MAKRO<<8) & 0xFF00)


Wo ist der Unterschied zu

#define NOCHEINNEUESMAKRO (MAKRO<<8)


Was gewinnt man durch die UND-Operation an dieser Stelle?
Sinn scheint ja wohl zu sein, dass das untere Byte definitiv auf Null 
steht. In welcher Situation könnte es denn passieren, dass nach dem 
Shift hier keine Null im niederen Byte steht?

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Shift schrieb:
> In welcher Situation könnte es denn passieren, dass nach dem
> Shift hier keine Null im niederen Byte steht?

Ich kenne keine. Meines Wissens werden beim Left-Shift immer 0en 
reingeschoben. Anders ist es beim Right-Shift im Zusammenhang mit 
vorzeichenbehafteten Variablen.

von Clemens L. (c_l)


Lesenswert?

Shift schrieb:
> Was gewinnt man durch die UND-Operation an dieser Stelle?

Gar nichts.

Es wäre möglich, dass diese Makros automatisch generiert wurden.

Oder dass es andere Makros gibt, wo das UND notwendig ist, und es hier 
aus Konsistenzgründen beibehalten wurde.

von Peter D. (peda)


Lesenswert?

Ich würde mal sagen, das "& 0xFF00" löscht der Optimizer einfach, d.h. 
der erzeugte Code ist der gleiche.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Allerdings habe ich auf Wikipedia folgende interessante Bemerkung 
gefunden:

---------
Ebenso ist das Ergebnis laut C- und C++-Sprachnorm undefiniert, wenn die 
Anzahl der Bitverschiebungen größer oder gleich der Bitbreite der 
Rechenarchitektur ist.[2] Wird beispielsweise auf einer 
32-Bit-Architektur von Intel-Prozessoren gearbeitet (IA32), so bewirkt 
eine Verschiebung um 32 Stellen oft gar keine Veränderung des 
Ergebnisses, d. h. für x = y << 32 ergibt sich x == y. Der Grund liegt 
in der Art und Weise, wie die Compiler die Schiebeoperation in 
Maschinencode umsetzen.
---------

Und tatsächlich:
1
#include <stdio.h>
2
#include <stdint.h>
3
4
int main()
5
{
6
    uint32_t x = 0xFFFFFFFF;
7
8
    printf ("%u\n", x);
9
10
    x <<= 32;
11
12
    printf ("%u\n", x);
13
}

$ cc t.c && ./a.out
t.c: In function 'main':
t.c:10:5: warning: left shift count >= width of type
     x <<= 32;
     ^
4294967295
4294967295

Hier passiert tatsächlich - bis auf die Warnung - ... nichts!

Ersetze ich uint32_t durch uint8_t, initialisiere ich x mit 0xFF und 
schiebe nur um 8 Stellen, kommt aber tatsächlich nach dem Schieben 
wieder 0 raus.

Fazit: Das vom TO angegebene Makro könnte höchstens relevant sein auf 
Büchsen, bei denen uint8_t identisch mit unsigned int ist. Aber da 
bringt das Schieben um 8 Stellen sowieso nichts.

: Bearbeitet durch Moderator
von Falk B. (falk)


Lesenswert?

@ Frank M. (ukw) Benutzerseite

>Ebenso ist das Ergebnis laut C- und C++-Sprachnorm undefiniert, wenn die
>Anzahl der Bitverschiebungen größer oder gleich der Bitbreite der
>Rechenarchitektur ist.[2] Wird beispielsweise auf einer
>32-Bit-Architektur von Intel-Prozessoren gearbeitet (IA32), so bewirkt
>eine Verschiebung um 32 Stellen oft gar keine Veränderung des
>Ergebnisses, d. h. für x = y << 32 ergibt sich x == y. Der Grund liegt
>in der Art und Weise, wie die Compiler die Schiebeoperation in
>Maschinencode umsetzen.
>---------

Das halte ich für ein Gerücht. Sowohl was die Rechenarchitektur als auch 
das Verhalten bei IA32 angeht.

Auch der AVR kann 32, ja sogar 64 Bit Variablöen verarbeiten, und das 
macht er auch korrekt! Eher ist so, das ein (konstantes) Lichtsschieben 
um mehr Bits als die Variable umfasst immer 0 ergibt, ggf. sogar 
optimiert ohne echtes Schieben. Der Compiler mag eine Warnung ausgeben, 
aber er MUSS die Schiebeoperation ausführen!

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Falk B. schrieb:
> Der Compiler mag eine Warnung ausgeben,
> aber er MUSS die Schiebeoperation ausführen!

Du meinst also, gcc hat hier einen Bug?

Ich glaube sogar, dass gcc die Schiebeoperation durchführt, aber der 
Prozessor nicht. ;-)

Nochmal die Ausgabe:
1
$ cc t.c && ./a.out
2
t.c: In function 'main':
3
t.c:10:5: warning: left shift count >= width of type
4
     x <<= 32;

Der Compiler meckert also genau den Fall an, der bei Wikipedia erwähnt 
ist.

Output:
1
4294967295
2
4294967295

0xFFFFFFFF um 32 Stellen nach links geschoben ergibt also 0xFFFFFFFF.
1
# gcc --version
2
gcc (Debian 4.9.2-10) 4.9.2
1
# cat /proc/cpuinfo | grep model
2
...
3
model name      : Intel(R) Core(TM) i7-2600 CPU @ 3.40GHz

: Bearbeitet durch Moderator
von Falk B. (falk)


Lesenswert?

@ Frank M. (ukw) Benutzerseite

>> Der Compiler mag eine Warnung ausgeben,
>> aber er MUSS die Schiebeoperation ausführen!

>Du meinst also, gcc hat hier einen Bug?

Keine Ahnung, aber es würde mich schon arg wundern, wenn DAS ein 
normgerechtes Verhalten von C wäre. Wobei, es ist ja C . . . . 8-0

>Ich glaube sogar, dass gcc die Schiebeoperation durchführt, aber der
>Prozessor nicht. ;-)

Ein 2. FDIV Bug? ;-)

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Falk B. schrieb:
> Ein 2. FDIV Bug? ;-)

Wohl eher ein Feature ;-)

Zitat von:

The C programming language:

The result is undefined if the right operand is negative, or greater 
than or equal to the number of bits in the left expression’s type.

Zitat von

IA-32 Intel Architecture Software Developer’s Manual 3

The 8086 does not mask the shift count. However, all other IA-32 
processors (starting with the Intel 286 processor) do mask the shift 
count to 5 bits, resulting in a maximum count of 31. This masking is 
done in all operating modes (including the virtual-8086 mode) to reduce 
the maximum execution time of the instructions.

-----
Der Compiler muss also gar nichts bei Left-Shifts >= 32 (im Fall von 
uint32_t).

EDIT:

Sowohl auf einer Linux-Büchse mit ELF 32-bit als auch als ELF 64-bit 
Executable ist das Verhalten identisch.
1
# file a.out
2
a.out: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.15, not stripped

: Bearbeitet durch Moderator
von Falk B. (falk)


Lesenswert?

Also mal wieder so ein "tolles" Feature von C, ich hab's geahnt.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Falk B. schrieb:
> Also mal wieder so ein "tolles" Feature von C, ich hab's geahnt.

Aber so gut wie nicht relevant. Ich glaube nicht, dass wir beide jemals 
auf die Idee kommen werden, einen 32-Bit-Wert um 32 Bit oder mehr zu 
shiften. Das hört sich doch schon so ziemlich sinnlos an.

von Falk B. (falk)


Lesenswert?

@  Frank M. (ukw) Benutzerseite

>Aber so gut wie nicht relevant. Ich glaube nicht, dass wir beide jemals
>auf die Idee kommen werden, einen 32-Bit-Wert um 32 Bit oder mehr zu
>shiften. Das hört sich doch schon so ziemlich sinnlos an.

Als konstante Verschiebung eher nicht, aber als variable Verschiebung 
ist das durchaus denkbar. In dem Fall kann man nur hoffen, daß die 
variable Verschiebung als Mehrfachverschiebung umgesetzt wird und somit 
zum erwarteten Ergebnis führt. Schön ist das aber nicht.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Falk B. schrieb:
> Als konstante Verschiebung eher nicht, aber als variable Verschiebung
> ist das durchaus denkbar.

Ja, zum Beispiel in einer Schleife das Verschieben um eins.

Oder (ungünstig ohne Barrel-Shifter) das Verschieben mit:
1
for (i = 0; i < sizeof (uint32_t); i++)
2
{
3
    if (irgendwas & (1<<i))
4
    {
5
       setze_bit_auf_1();
6
    }
7
    else
8
    {
9
       setze_bit_auf_0();
10
    }
11
}

Aber auch da läuft das i nur bis 31 und nicht 32. Von daher keine 
Gefahr. Wenn ich tatsächlich einen Wert von 32 Bit um mehr als 31 Bit 
variabel verschiebe, dann betrachte ich das als Bug in meinem Programm 
- nicht als Bug im Compiler.

von Falk B. (falk)


Lesenswert?

@Frank M. (ukw) Benutzerseite

>Ja, zum Beispiel in einer Schleife das Verschieben um eins.

>Oder (ungünstig ohne Barrel-Shifter) das Verschieben mit:

>for (i = 0; i < sizeof (uint32_t); i++)

Da fehlt wohl ein *8, denn die Schleife ist nach 4 Durchläufen beendet.

{
>    if (irgendwas & (1<<i))

Ich bin ein Fan von Bitmasken, vor allem auf dem AVR. Ist deutlich 
schneller.

>Aber auch da läuft das i nur bis 31 und nicht 32. Von daher keine
>Gefahr. Wenn ich tatsächlich einen Wert von 32 Bit um mehr als 31 Bit
>variabel verschiebe, dann betrachte ich das als Bug in meinem Programm
>- nicht als Bug im Compiler.

Hmm, auch ein Blickwinkel 8-0

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Falk B. schrieb:

> Da fehlt wohl ein *8, denn die Schleife ist nach 4 Durchläufen beendet.

Stümmt :-)

von Jim M. (turboj)


Lesenswert?

Shift schrieb:
> #define MAKRO 3
> #define NEUESMAKRO ((MAKRO<<8) & 0xFF00)
>
> Wo ist der Unterschied zu
>
> #define NOCHEINNEUESMAKRO (MAKRO<<8)

Das Ergebnis wird auf die 16 Bits geclippt, wenn MAKRO mal größer als 
255 ist. Bei 32 Bit Registern wäre das dann nicht mehr egal. Das macht 
man oft wenn die höherwertigen Bits eine andere Funktion haben.

Und ja, im Beispiel überflüssig und spätestens vom Optimizer entfernt.

von (prx) A. K. (prx)


Lesenswert?

Frank M. schrieb:
> Fazit: Das vom TO angegebene Makro könnte höchstens relevant sein auf
> Büchsen, bei denen uint8_t identisch mit unsigned int ist.

Weniger als 16 Bits für int/unsigned sind nicht zum C Standard konform.

: Bearbeitet durch User
von Paul B. (paul_baumann)


Lesenswert?

Frank M. schrieb:
> Ich glaube nicht, dass wir beide jemals
> auf die Idee kommen werden, einen 32-Bit-Wert um 32 Bit oder mehr zu
> shiften.

Man soll ja Nichts auf die lange Bank schieben.
:)
MfG Paul

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.