Signed to Unsigned int



  • Hallo,

    ein CAN ist ein Controller Area Network, ein Bussystem, mit dem Daten übertragen werden können.

    soweit ich weiß, werden die Daten intern in hex gehändelt und zum verschicken, bekommen sie den Datentyp, der in der jeweiligen Spezifikation steht.
    Bei mir sind es 12 Bit signed integer.
    davon ist 0x800 die ungültigkeitsbezeichnung.

    ich hab irgendwie das problem, dass wer auch immer - BS, compiler ... - nicht erkennt, wenn eine negative binär/hexzahl ankommt 😞

    Der Rohwert bei meiner Simulation ist -145 und dieser Rohwert sollte bei mir auch ankommen, aber es kommt als Rohwert leider 3951 raus 😞



  • Naja, das geht ganz genauso wie auf dem Papier. Das MSB hat im Gegensatz zu den restlichen Bits negatives Vorzeichen, das ist alles.

    if (value&(1<<(value_bits-1)))value-=(1<<value_bits);
    


  • Ich fürchte hier geht mal wieder interne Repräsentation der Zahlen und Art der Darstellung durcheinander. Intern werden alle Zahlen binär im Speicher hinterlegt. Bei der Ausgabe kann man dann die Zahl Dezimal oder Hexadezimal oder was auch immer darstellen. Wenn du sagst die Zahlen werden nach Hex geändert glaube ich das fast nicht. Wird die Zahl wirklich in einen String der 3 Hex Zeichen hält konvertiert und diese 3 Zeichen dann übertragen? Oder werden doch eher 2 Byte binär übertragen? Am besten zeigst du uns mal den Code der die Daten über CAN versendet und wieder einlesen soll.



  • Und wieder jemand, der keinen (bzw. kaum) Plan von CAN hat...

    CAN-Daten bestehen einfach aus einer Folge von 1-8 Bytes und die Interpretation der Daten wird entsprechend der PGN (parameter group number) durchgeführt (zumindestens bei J1939 und ISOBUS).

    Wie soll auch jemals eine negative Zahl in der Funktion ankommen, wenn du den Parameter als unsigned deklarierst (ich nehme mal an tU32 steht für 'unsigned int 32'?

    Am besten bietet sich dafür dann ein Bitfeld an (je PGN), welches du dann aus den ankommenden Daten entsprechend umwandelst (castest).



  • Hallo th69,

    ja, ich habe wenig Ahnung vom CAN, da ich gerade erst damit beginne mich damit zu beschäftigen. ⚠

    Ja, das war auch mein Problem.
    Die Daten vom CAN werden über eine firmeninterne API empfangen und ich erhalte die Daten als tU32, was ich ebenfalls doof finde.

    d.h. aber, dass ich es selber umwandeln muss, aber ich weiß noch nicht wie 😞



  • C++_Greenhorn schrieb:

    d.h. aber, dass ich es selber umwandeln muss, aber ich weiß noch nicht wie 😞

    -> in signed speichern -> Stellenwert des MSB verrechnen.
    Wie oft soll man dir das noch erklären bis du aufhörst, die gleiche Frage zu stellen?



  • Wie gesagt, benutze ein Bitfeld, z.B.

    struct MyData
    {
      // evtl. noch mit Paddingdaten (Bits) auffüllen, z.B. int padding:20
      int data:12;
    };
    
    MyData myData = (MyData)u32Value;
    
    int value = myData.data;
    

    Mit welchem CAN-Protokoll arbeitest du denn?



  • Hallo,

    hab gerade meinen Kollegen gefragt, der weiß es auch ned genau:
    entweder CanOpen oder J1939.



  • GiraffeTurboSetzen schrieb:

    C++_Greenhorn schrieb:

    d.h. aber, dass ich es selber umwandeln muss, aber ich weiß noch nicht wie 😞

    -> in signed speichern -> Stellenwert des MSB verrechnen.
    Wie oft soll man dir das noch erklären bis du aufhörst, die gleiche Frage zu stellen?

    Wie oft muss ich das noch erklären, dass ich eine binäre Zahlenfolge als int übergeben bekomme, mit der dieser Befehl nicht funktioniert ???



  • C++_Greenhorn schrieb:

    Wie oft muss ich das noch erklären, dass ich eine binäre Zahlenfolge als int übergeben bekomme, mit der dieser Befehl nicht funktioniert ???

    Was soll daran nicht funktionieren?

    Naja, hat kein Wert mit jemandem zu diskutieren, der nicht lernfähig ist.
    Bitte lass das einfach mit dem Programmieren.



  • C++_Greenhorn schrieb:

    Wie oft muss ich das noch erklären, dass ich eine binäre Zahlenfolge als int übergeben bekomme, mit der dieser Befehl nicht funktioniert ???

    Es funktioniert. Rechne doch einfach nach. Wenn das elfte Bit gesetzt ist (ist in deinem Beispiel) wird von der Zahl 2^12 = 4096 abgezogen. Jetzt setzen wir ein: 3951-4096 = −145. Also genau was du möchtest. Musst du natürlich mit signed ints rechnen.



  • @ sebi707
    Danke für deine kurze Rechnung! Die hat es mir einfacher gemacht,
    GiraffeTurboSetzen's Idee nachzuvollziehen. Jetzt habe ich einen lauffähigen Code,der mit Dezimalzahlen genau diese Idee umsetzt.

    @GiraffeTurboSetzen
    Ggf. habe ich mich umständlich ausgedrückt, daher will ich das Problem nochmal
    genauer beleuchten.
    Ich bekomme von meinem Bus eine Integer-Zahl, die jedoch keine übliche
    Interpretation zulässt. Wenn ich ein int value = 0110 bekomme, versteht in diesem
    Fall mein Interpreter durch die vorangestellt „0“ einen Hexadezimal-Wert und würde
    mir bei einer Ausgabe ohne weiteres Zutun den Wert 272 (also 0x0110 zu 272
    dezimal) ausgeben. Habe ich keine führende Null besteht das Problem
    beispielsweise nicht, aber trotzdem hätte ich z.B. bei einem int value = 1100 eben
    dezimal den Wert 1100, obwohl ich diesen als 0b110 = 12 dezimal verarbeiten muss.
    Hierfür konnte ich mir durch geeignetes maskieren eine Funktion schreiben, die
    diese Problem löst.
    Der Knackpunkt war für mich der Fall, wenn ein int value = 1100 als
    vorzeichenbehaftete Binärzahl im Zweierkomplement interpretiert werden sollte. Wie
    das jetzt umgerechnet wird, war mir auf dem Papier klar, jeoch habe ich es nicht in
    Code umsetzen können, wie ich aus dem Zweierkomplement 0b1100 (aber immer
    noch dezimal 1100 für den Interpreter!) auf den Dezimalwert -4 komme. Wenn ich in
    deine (GiraffeTurboSetzen) genannte if-Abfrage für value = 1100 und für value_bits =
    4 einsetze, dann stünde ausgerechnet folgendes da:

    if ( 8 ) {
    value = 1084;
    }
    

    und das ist mitnichten das Ergebnis, das ich erhofft hatte.
    Wie gesagt, die Idee ist die richtige, jedoch hast du mein Problem nicht richtig
    erkannt…

    Jedoch schlage ich dir bei deinem Tonfall vor, das Schreiben in Foren sein zu lassen, wenn du Leute, die sich momentan nicht sofort richtig artikulieren können und einfach den Wald vor lauter Bäumen nicht sehen, so scharf runtermachst. ⚠ ⚠



  • Sorry C++Greenhorn, aber das wird ja immer abstruser, was du schreibst.
    Du scheinst, wie sebi707 schon geschrieben hat, überhaupt nicht zu verstehen (oder es nicht richtig hier formulieren zu können), wie Daten gespeichert werden (intern gibt es nur binär - alles andere, was du beschreibst, ist lediglich eine (menschliche) Interpetation der Daten).

    Was für ein Wert soll jetzt "0110" darstellen? Und wieso erhältst du diesen als String (denn nur dann machen deine Aussagen halbwegs Sinn)?
    Und wer intepretiert ("mein Interpreter") diesen Datenwert???

    Ich dachte, du redest von einem CAN-Bus Signal (bzw. Message) und dann wäre es einfach eine Bit-(bzw. Byte-)Folge.
    Auch wenn deine externe Library dir den Wert als tU32 übergibst, die Interpretation mußt du dann schon selber durchführen. Und wie ich schon schrieb, wäre dann z.B. ein Bitfeld dafür passend.

    PS: Negative und positive Zahlen sind auch nur eine Interpretation der Daten (welche nur bei mathematischen Operationen oder einer Ausgabe relevant sind).



  • .. man sollte ja gar nicht meinen, wie schwierig so eine Bit-Popelei ist.

    Folgender Code funktioniert nur, wenn Du Dich auf einem little endian System befindet (z.B. Windows) und die 12-Bit-Zahl ganz vorn im Telegramm steht:
    Weiter nehmen ich an, dass die Quelle ein big endian-System ist. -145 ist ein schlechtest Beispiel, da sie in 12Bit spigelsymmetrisch ist, daher hab ich -146 genommen!

    #include <iostream>
    #include <cstdint>      // std::uint32_t, int16_t
    #include <algorithm>    // std::reverse
    using namespace std;
    
    typedef std::uint32_t tU32;
    void SignalKommt( tU32 u32Value )
    {
        cout << "Value: " << u32Value << endl;
        cout << "Value: 0x" << hex << u32Value << dec << endl;
    
        unsigned char* beg = reinterpret_cast< unsigned char* >( &u32Value );
        std::reverse( beg, beg + 2 ); // Byte-Reihenfolge auf little endian bringen
        auto zahl = *reinterpret_cast< std::int16_t* >( beg );    // in signed Zahl ablegen;
        zahl >>= 4;  // auf 16 Bit bringen
        cout << "Ergebnis: " << zahl << endl;
    }
    int main()
    {
        using namespace std;
                           // 1111 0110  1110 ... soll eine -146 sein!
        unsigned char can[] = { 0xf6, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };   // das CAN-Telegramm
        tU32 u32Value = *reinterpret_cast< const tU32* >( can );
        SignalKommt( u32Value );
        return 0;
    }
    

    Output:

    Value: 57590
    Value: 0xe0f6
    Ergebnis: -146



  • C++_Greenhorn schrieb:

    Ich bekomme von meinem Bus eine Integer-Zahl, die jedoch keine übliche
    Interpretation zulässt. Wenn ich ein int value = 0110 bekomme, versteht in diesem
    Fall mein Interpreter durch die vorangestellt „0“ einen Hexadezimal-Wert und würde
    mir bei einer Ausgabe ohne weiteres Zutun den Wert 272 (also 0x0110 zu 272
    dezimal) ausgeben. Habe ich keine führende Null besteht das Problem
    beispielsweise nicht, aber trotzdem hätte ich z.B. bei einem int value = 1100 eben
    dezimal den Wert 1100, obwohl ich diesen als 0b110 = 12 dezimal verarbeiten muss.
    Hierfür konnte ich mir durch geeignetes maskieren eine Funktion schreiben, die
    diese Problem löst.

    Wie schon mehrfach erwähnt, sind das alles Erfindungen deinerseits. Zahlen werden ausschließlich binär gespeichert und verarbeitet. Wenn du mit std::cout eine dezimale Ausgabe siehst, dann liegt das nicht etwa daran, dass sie irgendwie "dezimal gespeichert wurde", sondern daran, dass während der Ausgabe mehrfach durch 10 geteilt wird und so die einzelnen Dezimalstellen errechnet werden.

    Kehren wir zu deinem Beispiel mit 3951 und -145 zurück.
    Tatsache ist offenbar, dass dir der Wert in einer 32 bit-Variable übergeben wird.

    Du hast also folgendes - die niederen 12 bit sind gesetzt, der Rest sind Nullen:
    uVal=00000000000000000000111101101111

    Dein Problem ist, dass das höchste Bit (fett markiert) im Kontext eines unsigned 32 bit-Typen die Wertigkeit 2048 hat. Da du das aber als 12 bit-Zahl im Zweierkomplement interpretieren möchtest, wäre -2048 korrekt. Ergo musst du 2048-(-2048)=4096 wieder abziehen.

    Da du eine vorzeichenbehaftete Zahl darstellen willst:

    int32_t sVal=uVal;
    

    Es hat sich nichts verändert, außer dass das höchste Bit nun die Wertigkeit -2147483648 statt +2147483648 hat. Ist egal, weil dieses Bit immer 0 ist, wenn deine Zahl kürzer als 32 bit ist. Wenn nicht, bist du hiermit fertig und musst den nächsten Schritt überspringen (der würde im Falle von valueBits==32 undefined behavior verursachen).

    const int valueBits=12;
    if (uVal&(1<<(valueBits-1))) { //höchste Bit gesetzt?
        sVal-=1<<valueBits; //4096 abziehen
    }
    

    Als Ergebnis hast du nun den gewünschten Wert -145. Binär sieht das nun so aus:
    11111111111111111111111101101111
    Letzendlich hast du eine 12 bit-Zahl im Zweierkomplement durch eine sign extension auf eine 32 bit-Zahl im Zweierkomplement erweitert - genau das, was erreicht werden sollte.



  • Werner Salomon schrieb:

    .. man sollte ja gar nicht meinen, wie schwierig so eine Bit-Popelei ist.

    Ich weiß nicht ob der folgende Code als negativ Beispiel gedacht war oder dem TO helfen sollte. Jedenfalls werden dort viele Annahmen gemacht, die nicht überall zutreffen. Das es nur auf einem Little Endian System läuft hast du ja selbst schon erkannt. Der Shift nach rechts von einem signed Integer ist auch nicht festgelegt. Besser den Code von GiraffeTurboSetzen nutzen.



  • sebi707 schrieb:

    Besser den Code von GiraffeTurboSetzen nutzen.

    blöd bloß, dass der nicht funktioniert, wenn beide Systeme unterschiedlichen Endian haben. Der OP scheint auch der Meinung zu sein, dass das bei ihm nicht funktioniert.

    @sebi707: Mach Du doch doch mal einen Vorschlag, wie man die Zahl Endian-sicher auslesen kann.



  • Die Anpassung von GiraffeTurboSetzen macht gar nichts mit der Endianness weil das schon den Schritt davor passiert sein sollte. So könnte der Schritt davor platformunabhängig aussehen:

    uint8_t can[] = { 0xf6, 0xe0 };
    int value = can[0] << 4 | can[1] >> 4;
    
    // Anpassung für negative Zahlen von GiraffeTurboSetzen
    const int valueBits = 12;
    if(value & (1 << (valueBits - 1)))
      value -= 1 << valueBits;
    

    Wenn die gesendeten Daten Little Endian sind tauscht man eben can[0] und can[1] .


Anmelden zum Antworten