Forum: PC-Programmierung Nochmal Frage zu logischer Verknüpfung int | Byte


von freundlicher gast (Gast)


Lesenswert?

Der Typ "int" kann nicht implizit in "byte" konvertiert werden. Es ist 
bereits eine explizite Konvertierung vorhanden. (Möglicherweise fehlt 
eine Umwandlung.) (CS0266) - \DIO_setzen.cs:20,47

Dieser Fehler kommt, wenn ich die MAske mit dem Modulstatus verknüpfen 
möchte (sowohl | als auch &~)

Mit dem Convert-Befehl habe ich schon einiges probiert, aber leider 
kommen da nur andere Fehler. Ein Byte sind doch 8 Bit und meine Maske 
hat 32 Bit, also 4 Byte. Wieso kann man 4 Byte nicht mit einem Byte 
maskieren?
1
using System;
2
3
namespace CAN_UIR
4
{
5
  public class IO_Operatoren
6
  {
7
    public static void DIO_setzen(int Modul, int Kanal, int Status)
8
    {
9
      int maske=0;
10
  
11
      for (int bit = 1; bit<8; bit++)
12
        {
13
          double maske_d = Math.Pow(2, bit);
14
          maske = Convert.ToInt32(maske_d);  
15
        }  
16
      
17
      switch (Status)
18
      {
19
        case 1:
20
          MainForm.tx_modulstatus_neu[Modul, 0] = (MainForm.tx_modulstatus[Modul, 0] | maske);                    //FEHLER
21
              MainForm.tx_modulstatus[Modul, 0] = MainForm.tx_modulstatus_neu[Modul, 0]; 
22
          MainForm.can_write((Modul), 1, (MainForm.tx_modulstatus_neu[Modul, 0]), 0, 0, 0, 0, 0, 0, 0);
23
                break;
24
            
25
            
26
            case 0:
27
          MainForm.tx_modulstatus_neu[Modul, 0] = (MainForm.tx_modulstatus[Modul, 0] &~ maske);
28
              MainForm.tx_modulstatus[Modul, 0] = MainForm.tx_modulstatus_neu[Modul, 0]; 
29
          MainForm.can_write((Modul), 1, (MainForm.tx_modulstatus_neu[Modul, 0]), 0, 0, 0, 0, 0, 0, 0);
30
                break;
31
      }
32
    }
33
  }
34
}

von Sam .. (sam1994)


Lesenswert?

Das liegt an c#. Normalerweise mag ich dir Sprache, aber hier wurde echt 
Mist gebaut. Alle logischen Operatoren sind nur in Int definiert (glaub 
ich aufjedenfall). Deswegen gibt er dir auch ein Int zurück. Um die das 
Ergebnis nun nach byte zu konvertieren musst du den ganzen Block in eine 
Klammer schreiben (hast du ja schon gemacht) und davor den Cast nach 
byte setzen.

von Jasch (Gast)


Lesenswert?

freundlicher gast schrieb:
> Der Typ "int" kann nicht implizit in "byte" konvertiert werden. Es ist
> bereits eine explizite Konvertierung vorhanden. (Möglicherweise fehlt
> eine Umwandlung.) (CS0266) - \DIO_setzen.cs:20,47

Ich gehe mal davon aus Du redest hier von Code in C#?

> Dieser Fehler kommt, wenn ich die MAske mit dem Modulstatus verknüpfen
> möchte (sowohl | als auch &~)

Tja, im Zweifel hat der Compiler bei sowas Recht.

"Modulstatus" ist vermutlich byte oder sowas?

> Mit dem Convert-Befehl habe ich schon einiges probiert, aber leider
> kommen da nur andere Fehler. Ein Byte sind doch 8 Bit und meine Maske
> hat 32 Bit, also 4 Byte. Wieso kann man 4 Byte nicht mit einem Byte
> maskieren?

Weil dabei 24 Bits irgendwie übrigbleiben, überstehen, wie immer Du es 
nennen willst? Die bitweisen Operatoren sind genau was der Name sagt, 
Bit-weise, nicht anwendbar wenn die Anzahl Bits in den Operanden nicht 
übereinstimmt.

>
1
[snip]
2
>

Wie man sowas machen könnte:

byte modulstatus;
int maske;

...
maske = /* something */;

modulstatus = modulstatus | (byte) maske;

Das ist natürlich ungetestet, so spät am Abend... Noch besser: wieso die 
Maske als int definieren wenn man eh nur 8 Bits Maske braucht?

von Jasch (Gast)


Lesenswert?

Uuhhh, habs nochmal nachgesehen...

Samuel K. schrieb:
> Das liegt an c#. Normalerweise mag ich dir Sprache, aber hier wurde echt
> Mist gebaut. Alle logischen Operatoren sind nur in Int definiert (glaub
> ich aufjedenfall).

Das stimmt nicht ganz, man kann sie auf alle Integer-Type anwenden.

> Deswegen gibt er dir auch ein Int zurück. Um die das
> Ergebnis nun nach byte zu konvertieren musst du den ganzen Block in eine
> Klammer schreiben (hast du ja schon gemacht) und davor den Cast nach
> byte setzen.

Ja, das muss man so machen, weil der Operator '|' seine Operanden 
mindestens zu int (hoch-)konvertiert und so auch einen int zurückgibt.

Finde ich jetzt ja doof, andererseits ist C# nicht für µC gemacht 
worden.

von freundlicher gast (Gast)


Lesenswert?

Samuel K. schrieb:
> und davor den Cast nach
> byte setzen.

und wie sieht das dann aus? Ist das sowas wie Convert?

Jasch schrieb:
> Ich gehe mal davon aus Du redest hier von Code in C#?

richtig, ich rede von C#

Jasch schrieb:
> Noch besser: wieso die
> Maske als int definieren wenn man eh nur 8 Bits Maske braucht?

die Maske ist als Int, aber auch als Byte geht das nciht.

von Sam .. (sam1994)


Lesenswert?

x = (byte)(y|z);

Alternativ kann man sich eine eigene Byte Klasse basteln. Wenn man dabei 
den =operator überlädt ist das ganz flexibel.

PS: Ich hab das damals selbst rausgefunden in dem ich alle logisch 
möglichen Klammerkombinationen probiert habe. Wenn man dann die korrekte 
gefunden hat, weiß man meistens was der Fehler war.

Problem ist (byte)x|z konvertiert zwar x zu einem Byte, | gibt aber 
wieder einen Int zurück. Genauso mit z. Deswegen um das ganze eine 
Klammer und nur das Ergebnis wird gecastet.

von freundlicher gast (Gast)


Lesenswert?

Samuel K. schrieb:
> x = (byte)(y|z);

so wie Du das beschrieben hast, funktioniert es ganz gut. Habe es dabei 
belassen. Danke.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

1
int maske=0;
2
3
for (int bit = 1; bit<8; bit++)
4
{
5
  double maske_d = Math.Pow(2, bit);
6
  maske = Convert.ToInt32(maske_d);  
7
}
Sowas ist natürlich ziemlich schrecklich.

Zunächst ist der Wertebereich der Schleife verdächtig, die wird hier 
nämlich genau 7 mal durchlaufen ("bit" deckt dabei den Zahlenbereich von 
1 bis 7 ab).

Dann ist der "Algorithmus" zur Bestimmung der Wertigkeit eines Bits 
etwas, äh, ineffizient.

Das hier reicht:
1
  maske = 1 << bit;

Und abschließend noch: Was soll das ganze eigentlich?

Bevor es in der Funktion DIO_Setzen irgendwie weitergeht, wird diese 
Schleife da sieben mal ausgeführt. Und in "maske" steht immer der 
letzte so aufwendig "berechnete" Wert, nämlich 1 << 7 bzw. 2^7.

Warum aber wurden vorher die Werte 1 << 1 bis 1 << 6 resp. 2^1 bis 2^6 
"berechnet"?

Und dann geht es weiter:
1
switch (Status)
2
{
3
  case 1:
4
    MainForm.tx_modulstatus_neu[Modul, 0] = (MainForm.tx_modulstatus[Modul, 0] | maske);                    //FEHLER
5
    MainForm.tx_modulstatus[Modul, 0] = MainForm.tx_modulstatus_neu[Modul, 0]; 
6
    MainForm.can_write((Modul), 1, (MainForm.tx_modulstatus_neu[Modul, 0]), 0, 0, 0, 0, 0, 0, 0);
7
    break;
8
9
  case 0:
10
    MainForm.tx_modulstatus_neu[Modul, 0] = (MainForm.tx_modulstatus[Modul, 0] &~ maske);
11
    MainForm.tx_modulstatus[Modul, 0] = MainForm.tx_modulstatus_neu[Modul, 0]; 
12
    MainForm.can_write((Modul), 1, (MainForm.tx_modulstatus_neu[Modul, 0]), 0, 0, 0, 0, 0, 0, 0);
13
    break;
14
}

Das lässt sich auch etwas reduzieren:
1
switch (Status)
2
{
3
  case 1:
4
    MainForm.tx_modulstatus[Modul, 0] |= maske;
5
    break;
6
7
  case 0:
8
    MainForm.tx_modulstatus[Modul, 0] &= ~maske;
9
    break;
10
}
11
12
MainForm.tx_modulstatus_neu[Modul, 0] = MainForm.tx_modulstatus[Modul, 0];
13
14
MainForm.can_write((Modul), 1, (MainForm.tx_modulstatus_neu[Modul, 0]), 0, 0, 0, 0, 0, 0, 0);

Wenn "tx_modulstatus_neu" nur deswegen eingeführt wurde, weil es 
C#-Probleme mit den bitweisen Verknüpfungen gab, ließe sich die erste 
Zeile nach dem switch/case-Statement entfernen, can_write ist 
entsprechend dann tx_modulstatus anstelle von tx_modulstatus_neu zu 
übergeben.

Wie wir weiter oben gesehen haben, geht es bei "maske" ausschließlich um 
den Wert 0x80 bzw. 128, also lässt sich obiges switch/case auch so 
ausdrücken:
1
switch (Status)
2
{
3
  case 1:
4
    MainForm.tx_modulstatus[Modul, 0] |= 0x80;
5
    break;
6
7
  case 0:
8
    MainForm.tx_modulstatus[Modul, 0] &= ~0x80;
9
    break;
10
}

Sofern "Status" nie andere Werte als 0 oder 1 annehmen kann, ist 
natürlich ein switch/case hier völlig fehl am Platze:
1
if (Status == 1)
2
  MainForm.tx_modulstatus[Modul, 0] |= 0x80;
3
else
4
  MainForm.tx_modulstatus[Modul, 0] &= ~0x80;

Ein Lobgesang auf die Vorzüge von C# ist diese Vorstellung hier 
jedenfalls nicht. Just die Tage habe ich die gleichen 
math.pow-Verrenkungen bei einem jungen (naja, noch nicht 30jährigen) 
Java-Programmierer gesehen und musste schon schlucken.

von Arc N. (arc)


Lesenswert?

Rufus Τ. Firefly schrieb:
> Ein Lobgesang auf die Vorzüge von C# ist diese Vorstellung hier
> jedenfalls nicht. Just die Tage habe ich die gleichen
> math.pow-Verrenkungen bei einem jungen (naja, noch nicht 30jährigen)
> Java-Programmierer gesehen und musste schon schlucken.

Weil das anscheinend nicht mehr richtig gelehrt wird und/oder nicht mehr 
interessiert...
Hinzu kommt Javas Unsigned-Problem* (aber es gibt einen unsigned right 
shift >>>).
Das es in C# wie auch in Java bei solchen Sachen keine impliziten 
Typkonvertierungen gibt, sehe ich eher als Vorteil (die subtilen 
Unterschiede mal außen vor... double to int ist in Java null wenn der 
Wert NaN war, in C# int.MinValue, dafür kennt Java keinen checked 
context wo es eine OverflowException geben würde)

* "Quiz any C developer about unsigned, and pretty soon you discover 
that almost no C developers actually understand what goes on with 
unsigned, what unsigned arithmetic is." Gosling in einem Interview
http://www.gotw.ca/publications/c_family_interview.htm

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

Arc Net schrieb:
> Hinzu kommt Javas Unsigned-Problem

Java hat überhaupt kein Problem mit Unsigned, nur die Programmierer die 
arithmetische und bitoperatione wild durcheinanderwürfeln ohne 
nachzudenken und hoffen durch "unsigned" würden ihre Probleme 
verschwinden ;)

von Arc N. (arc)


Lesenswert?

Läubi .. schrieb:
> Arc Net schrieb:
>> Hinzu kommt Javas Unsigned-Problem
>
> Java hat überhaupt kein Problem mit Unsigned,

Stimmt es gibt keine unsigned integer

> nur die Programmierer die
> arithmetische und bitoperatione wild durcheinanderwürfeln ohne
> nachzudenken und hoffen durch "unsigned" würden ihre Probleme
> verschwinden ;)

das ist eher das selbe Problem das die Befürworter von dynamisch 
typisierten Sprachen nicht sehen wollen: Weniger Typen (d.h. bei 
dynamisch typisierten Sprachen nur einer), sind keine Hilfe, sondern 
schränken ein.
U.a. da zum einen auf alles mögliche geprüft werden muss, zum anderen 
muss mehr annotiert werden, da man z.B. wie bei unsigned/signed nicht 
von vornherein sehen kann, was die Funktion erwartet bzw. zurückliefert.

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

Arc Net schrieb:
> Weniger Typen

Eine Vielzahl (primitiver) Typen ist da aber auch nicht gerade 
förderlich und es macht das Maschinenmodell unötig komplex.

Arc Net schrieb:
> U.a. da zum einen auf alles mögliche geprüft werden muss

Na man kann es auch übertreiben, bestenfalls muss man auf > -1 prüfen...
... hingegen hat sich noch niemand beschwert das es keinen Typen gibt, 
welcher nur negativ sein kann, wieso also immer dieser ruf nach "nur 
positiven"?

Arc Net schrieb:
> nicht von vornherein sehen kann,
> was die Funktion erwartet bzw. zurückliefert
Dann ist die Funktion ungenügend dokumentiert, einer
1
void blub(unsigned int);
 seh' ich auch nicht an, dass sie in Wirklichkeit nur Zahlen bis 60'000 
verarbeiten kann.

Und wenn wirklich alle Stricke reißen kann man sich immer noch eine 
Wrapper Klasse schreiben.

Außer zu Dokumentationszwecken hab ich auch noch nie "unsigned" Typen 
vermisst noch ein Beispiel gesehen wo diese unumgänglich nötig wären.

von freundlicher gast (Gast)


Lesenswert?

@Rufus Τ. Firefly: Erstmal vielen Dank für Deine kritischen Blicke! Habe 
ich es hier mit einem Profi-Programmierer zu tun? Oder Ist das "einfach 
nur" Erfahrung? ;-)

Rufus Τ. Firefly schrieb:
> Das hier reicht:  maske = 1 << bit;

Was tut das genau?

Rufus Τ. Firefly schrieb:
> Bevor es in der Funktion DIO_Setzen irgendwie weitergeht, wird diese
> Schleife da sieben mal ausgeführt. Und in "maske" steht immer der
> letzte so aufwendig "berechnete" Wert, nämlich 1 << 7 bzw. 2^7.

das habe ich auch gesehen, und die Schleife entfernt. Das war wirklich 
grober Unfug was ich da getan habe...
Das ganze sah inzwischen so aus:
1
public static void DIO_setzen(int Modul, int Kanal, int Status)
2
    {
3
      int maske=0;
4
      
5
      double maske_d = Math.Pow(2, Kanal-1);
6
      maske = Convert.ToInt32(maske_d);  
7
      
8
      switch (Status

Wobei ich mir jetzt Deine Ratschläge nochmal zu Gemüt führen werde, und 
den Rest auch verbessere.

Rufus Τ. Firefly schrieb:
> ie wir weiter oben gesehen haben, geht es bei "maske" ausschließlich um
> den Wert 0x80 bzw. 128, also lässt sich obiges switch/case auch so
> ausdrücken:

Das ist nicht so, ich muss jedes Byte mit einem "Einheitsbyte" (so nenne 
ich es einfach mal) maskieren. Also Prüfen, ob das entsprechende Bit 0 
ist oder 1 ist.

Die Anwendung dahinter: Ich dende an ein CAN I/O-Modul ein Byte mit dem 
Zustand der 8 Ausgänge. Und wenn ich ein Bit setze, soll der Rest 
natürlich erhalten bleiben.

Danke nochmal für Deine Tipps, ich werde einiges korrigieren und dann 
nach und nach wieder Fragen stellen, wenns nciht klappt. (Ist immer 
schwer was zu verändern, wenn das Programm seine Funktion tut, aber ich 
will es ja technisch auch korrekt haben ;-)

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

freundlicher gast schrieb:
> Was tut das genau?

Siehe Bitmanipulation gilt auch für C# (und Java ;)

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

1
int maske=0;
2
      
3
double maske_d = Math.Pow(2, Kanal-1);
4
maske = Convert.ToInt32(maske_d);

lässt sich auch so formulieren:
1
int maske;
2
3
maske = 1 << (Kanal - 1);

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.