4 Bytes in eine Int32 Zahl (mit Vorzeichen) umwandeln

  • C++

Es gibt 19 Antworten in diesem Thema. Der letzte Beitrag () ist von dive26.

    4 Bytes in eine Int32 Zahl (mit Vorzeichen) umwandeln

    Das hier ist ein negativer Wert (I32) der in 4 Bytes daherkommt.
    Der Wert liegt geschätzt um die -1200 oder noch weniger.
    Ich habe aus der Messreihe einmal 3 Datensätze ausgelesen, die sollten sich nur geringfügig unterscheiden.

    HTML-Quellcode

    1. FF FF FC 94
    2. 255 255 252 148
    3. FF FF FC 92
    4. 255 255 252 146
    5. FF FF FC 94
    6. 255 255 252 148


    Einmal als Hex-Darstellung und einmal das selbe als DEC-Darstellung.

    Wie bekomme ich aus den 4 Bytes einen Wert mit Vorzeichen (der Wert kann positiv oder negativ sein)?

    Interessiert mich zwar auch für VB.NET, aber primär würde ich es für C++ (Arduino Code) benötigen.

    Habt Ihr da einen Tipp oder kurzen Code für mich?
    Ich versuche das schon seit mehreren Stunden.
    Liebe Grüße
    Roland Berghöfer

    Meine aktuellen und kostenlos verwendbaren Tools (mit VB.NET erstellt): freeremarkabletools.com | priconman.com | SimpleCalendar | AudibleTouch | BOComponent.com | bonit.at
    @dive26 Sieh Dir mal die union an: en.cppreference.com/w/cpp/language/union

    C-Quellcode

    1. union convert
    2. {
    3. int value;
    4. byte bytes[4];
    5. }
    6. convert xxx;
    7. xxx.bytes[0] = 0xFF;
    8. xxx.bytes[1] = 0xFF;
    9. xxx.bytes[2] = 0xFC;
    10. xxx.bytes[3] = 0x94;
    11. // Dein Wert wäre dann
    12. int MyValue = xxx.value;
    oder so ähnlich.
    In NET sieh Dir mal die BitConverter-Klasse an: learn.microsoft.com/en-us/dotn…bitconverter?view=net-7.0
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Vielen Dank lieber @RodFromGermany.
    Hab gerade auch was funktionierendes gefunden. Ist aber wohl eh das selbe was Du gepostet hast:

    (Meine Werte stehen im Byte-Array von Stelle 9 bis 12:

    C-Quellcode

    1. int32_t combined = (long)msg[12] << 0 | (long)msg[11] << 8 | (long)msg[10] << 16 | (long)msg[9] << 24;


    Klappt einwandfrei.
    Liebe Grüße
    Roland Berghöfer

    Meine aktuellen und kostenlos verwendbaren Tools (mit VB.NET erstellt): freeremarkabletools.com | priconman.com | SimpleCalendar | AudibleTouch | BOComponent.com | bonit.at

    dive26 schrieb:

    Ist aber wohl eh das selbe was Du gepostet hast:


    Nein, nicht wirklch. Das was du zeigst sind Bitwise Operations.

    Union ist nicht wie du dir vermutlich vorstellt. Im Speicher belegt diese gezeigte Union 4 Bytes, keine 8. Indem du so die einzelnen Bytes änderst, ändert sich auch der Integer Wert.
    Also ist der Integer Wert im RAM an Adresse 0x1000, sind die 4 Bytes vom Array an der gleichen. So kann man recht einfach über diese union durch ändern des int Wertes auch die Bytes ändern, genauso wie du durch ändern eines Bytes den int Wert auch veränderst. Man kann das so leichter nutzen ohne Bitwise Operations(oder sontiges) anwenden zu müssen um an die einzelnen Bytes zu kommen, oder auch wie in dem Fall um an eine int Darstellung zu kommen.

    Und hier geht kürzer;
    (long)msg[12] << 0
    das << 0 kannste sparen, damit wird nichts "verschoben"
    1 << 0 bleibt 1

    PS. @dive26
    Überlagert im Speicher wäre die kurze und einfache Erklärung.

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „BitBrösel“ ()

    Type-punning ist illegal; eigentlich generell, das einzige das erlaubt ist ist die Konvertierung zu char, std::byte.
    Zusätzlich erlaubt es der Standard nicht auf eine inaktive Region in einem union zuzugreifen. (die "Aktive" Region ist die, auf die das letzte mal geschrieben wurde)
    Nun, du bist auf Arduino und ich gehe mal aus du verwendest gcc-avr, GCC hat eine Erweiterung die es erlaubt dies zu tun, allerdings ist es alles andere als schön.

    Des weiteren, right-shift auf signed integer typen ist undefined behaviour wenn der sign bit davon irgendwie betroffen ist, daher sollte man auch das lassen.

    Viel eher würde ich dir empfehlen die Daten mit memcpy (oder wenn du Zugrif auf den Standard hast, was bei embedded meistens nicht der fall ist, std::memcpy) in den Typen zu kopieren.

    C-Quellcode

    1. char data[4];
    2. int32_t result;
    3. memcpy(&result, data, sizeof(int32_t));

    "memcpy" erlaubt es die Daten in andere typen zu kopieren.
    ----------------------------------------------------------------------------------------------------------------------

    Hier könnte meine Signatur stehen, aber die ist mir abfußen gekommen.

    ----------------------------------------------------------------------------------------------------------------------

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Elanda“ ()

    Wie oben erwähnt, per Standard ust es nicht legal, aus einem inaktiven union member zu lesen, das findet man auch in der cppreference Dokumentation (zugegeben, diese Dinge sind schwer zu finden):
    en.cppreference.com/w/cpp/language/union
    (Unter "Explanation" am Ende des ersten Paragraphen)

    Weiter, ist es dank der strikten Aliasing-Regeln in C++ Undefined Behaviour, auch mit "reinterpret_cast", zwei inkompatible Typen zu punnen (unter Type Aliasing):
    en.cppreference.com/w/cpp/language/reinterpret_cast

    Kurz exzerpiert, es ist erlaubt die Repräsentation eines Typen T1 als Typ T2 zu reinterpretieren, wenn folgendes zutrifft:
    1. T1 und T2 sind der selbe Typ, mit der gegenüberliegenden "signedness" als einzigen Unterschied
    2. Der reinterpretierte Typ ist entweder std::byte, char oder unsigned char
    3. Wenn T1 und T2 sich "ähnlich" sind, weiter unterteilbar in:
    3.1. sie sind der selbe Typ
    3.2. beides sind pointer und die auf die gezeigten Typen sind "ähnlich"
    3.3. Beide Typen sind Zeiger-zu-Member der selben Klasse und deren Typen sind "ähnlich"
    3.4. Beide Typen sind Arrays gleicher Größe oder beides unbekannter Größe und der Typ ist "ähnlich"
    3.5. Beide Typen sind Arrays gleicher Größe, oder eines mit unbekannter Größe und der Typ ist "ähnlich"

    Die letzten drei Punkte weichen bereits zu sehr ab, also ignorieren wir die mal.

    Sehen wir uns die anderen mal an:
    T1 ist byte[] T2 ist int32. (oder welcher Arduino Typ auch immer)
    byte könnte "unsigned char" sein, oder auch nicht, ist auch egal. (da es aber garantiert 8bit sind, ist es sehr warscheinlich)

    1. Trifft nicht zu, das eine ist ein array, das andere ein int typ, daher spielt auch die "signedness" keine Rolle
    2. Trifft auch nicht zu, T2 ist int32
    3.1. Sind definitiv nicht der selbe Typ
    3.2. Selbst wenn man sie in Pointer umwandelt, der Typ ohne den Pointer verweißt wieder Rekursiv auf 3.1 und 3.2

    Was könnte schlimmsten Falles passieren?
    Nun, da es Undefined Behaviour ist, so ziemlich alles mögliche.
    Darunter zum Beispiel nicht Ordnungsgemäß ausgerichteter Lese/Schreibzugriff (misaligned access, sehr problematisch für Platformen die Alignment sehr ernst nehmen), oder es könnte auch normal klappen; keiner weiß es.
    Das tückische an UB ist, in der nächsten Compiler Version könnte sich das Verhalten komplett verändert haben, vorher hat es funktioniert, jetzt nicht mehr.

    Nun, wie gesagt erlaubt Gcc das type punning über unions als eine Erweiterung und da AVR Teil der GCC ist, kannst du das auch tun.
    Allerdings ist es dennoch, gegenüber des C++ Standards, illegal. (C erlaubt es im übrigen)

    Mein Beitrag von oben hat auch nur eine legale Alternative aufgezeigt, ich habe nie gesagt man solle dies nicht tun, aber nicht jeder hier arbeitet mit Arduino bzw. GCC. Daher hatte ich den Bedarf darüber aufzuklären, für den Fall es wird von jemanden gelesen wessen Compiler dies nicht unterstützt. :)

    Meine Meinung: Man sollte sich auf gewisses Verhalten nicht verlassen wenn es der Standard nicht mit sich bringt.
    Der beste Keimboden für Bugs, aber das muss jeder für sich selbst entscheiden. ^^
    ----------------------------------------------------------------------------------------------------------------------

    Hier könnte meine Signatur stehen, aber die ist mir abfußen gekommen.

    ----------------------------------------------------------------------------------------------------------------------

    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „Elanda“ ()

    Nunja, ich denke auch das ist Geschmackssache. Ich nutze Unions generell selten, um Bytes zu einem int oder so umzuwandeln nutze ich auch Bitwise Operations. (bzw. in Net BitConverter)

    Im Fall von dive26 schreibt die Huawei Hardware ja explizit ein signed int irgendwo hin, was er ausliest. Daher sehe ich keine Gefahr von falschen Werten, wenn er Bitwise Operations nutzt, die Bytes sind halt nur das Medium zum speichern und/oder übertragen. Wären das nun "zufällige"(trifft es nicht ganz aber ich denke ich wisst wie ich es meine) Werten für die Bytes, ja dann könnte es zum Problem kommen wegen des Sign-Bits.

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „BitBrösel“ ()

    Elanda schrieb:

    Meine Meinung: Man sollte sich auf gewisses Verhalten nicht verlassen wenn es der Standard nicht mit sich bringt.
    Der beste Keimboden für Bugs, aber das muss jeder für sich selbst entscheiden.


    Ich glaube, du hast meinen Standpunkt nicht verstanden. Das, was du sagst, zu UB ist alles super und richtig, aber warum sollte ein memcpy besser sein? Nur weil die das in den Standard nicht reingeschrieben haben? Ich kann mir beim besten Willen kein Beispiel vorstellen, wo ein Pointercast nicht klappt, aber dann ein memcpy schon. Ich glaube, damit umgehst du nicht das Problem oder verhinderst Fehler, du sorgst nur dafür, dass hier nicht mehr "offiziell" steht: Das ist illegal.
    Ich glaube ich verstehe dich tatsächlich nicht, zu dem Pointer Cast habe ich bezüglich "strict aliasing" ja auch in den Beitrag geschrieben.

    Es steht ja im Standard als UB und UB ist immer schlecht.
    Ich kann dir mal einen Artikel dazu verlinken:
    en.cppreference.com/w/cpp/language/ub

    Außerdem ist memcpy auch die tatsächliche Empfehlung. (nicht des Standards aber von cppreference)

    Villeicht habe ich aber auch nur falsch verstanden was du in diesem Fall mit "pointercast" meinst.
    ----------------------------------------------------------------------------------------------------------------------

    Hier könnte meine Signatur stehen, aber die ist mir abfußen gekommen.

    ----------------------------------------------------------------------------------------------------------------------

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „Elanda“ ()


    Ob ich jetzt die Bytes nach rechts schiebe oder den Pointer nach links setzte, jedes missalignment würde beim Kopieren ja genau so mitgenommen werden. Warum sollte das kopieren besser sein? Er kann den float Pointer ja nicht anders interpretieren oder die Bytes beim Kopieren umsortieren.
    Bei dem Alignment-Problem Beispiel ging es darum, das man mit einem Typen auf eine Speicheradresse zugreift, welcher ein anderes Alignment Requirement haben könnte.
    Nehmen wir an ein char array hat ein Alignment Requirement von 1 byte und int32 von 4.
    Würde ich das char array jetzt mit einem int32 pointer mit einem alignment von 4 zugreifen, ist das ein Bruch der alignment-Vorraussetzungen.

    Ich habe hier einen Artikel gefunden, besser als der kann ich es nicht erklären:
    gist.github.com/shafik/848ae25ee209f698763cffee272a58f8

    Grundsätzlich aber geht es hier um Optimisationen.
    Compiler gehen, durch Annahme das der Entwickler die "strict aliasing" Regeln nicht bricht, davon aus, dass so etwas gar nicht erst passiert und führen anhand dessen gewisse Optimisationen durch.

    Wenn du die Daten aber anstelle dessen mit memcpy "kopierst" (das kopieren kann wegoptimiert werden, somit has du keinen Leistungs-Penalty), dann weiß der Compiler was du vorhast.
    Es geht hier also eher um die Sichtbarkeit deiner Intention.

    reinterpretieren: Der compiler geht davon aus du hältst dich and die Regeln.
    memcpy: Der Compiler weiß genau was du vorhast.
    ----------------------------------------------------------------------------------------------------------------------

    Hier könnte meine Signatur stehen, aber die ist mir abfußen gekommen.

    ----------------------------------------------------------------------------------------------------------------------

    Elanda schrieb:

    Grundsätzlich aber geht es hier um Optimisationen.


    Wenn das so wäre, dann ist der eine Weg besser als der andere, aber deswegen ist nicht einer von denen direkt illegal. Zu sagen 'du darfst etwas nicht machen' und 'es geht viel besser' ist für mich ein massiver Unterschied.
    Tut mir leid, du hast absolut recht, es klingt sehr verwirrend.
    Im Englischen nennt man C++ code der Standard-Konform ist oft umgangssprachlich "legal", daher habe ich mich daran gewöhnt auch im deutschen "legal" und "illegal" zu verwenden.
    Ich meinte nicht das es verboten ist, du wirst jetzt nicht verurteilt deswegen, sondern habe mich auf Konformität bezogen. (In dem Sinne, das es dann nicht mehr Konform ist, wäre es aber sehr wohl "verboten")

    Bedenke jedoch, dass diese Richtlinien eine Nutzen haben.
    Je eher Code sich Regeln unterordnet, desto eher wird garantiert Bugs zu verhindern.
    Man darf nicht vergessen, dass hinter diesen Regeln ein gesamtes Team voll von Spezialisten sitzt.

    Edit: Habe noch mal deine Antwort durch gelesen. Dieser Weg ist nicht nur besser sondern absolut zu bevorzugen.
    Durch etwaige Optimisationen können andere Werte resultieren die das Resultat verfälschen.
    Es geht nicht nur um Effizienz sondern auch um Richtigkeit der Daten.
    ----------------------------------------------------------------------------------------------------------------------

    Hier könnte meine Signatur stehen, aber die ist mir abfußen gekommen.

    ----------------------------------------------------------------------------------------------------------------------

    Dieser Beitrag wurde bereits 7 mal editiert, zuletzt von „Elanda“ ()

    Hallo Leute,

    vielen Dank für die rege Diskussion.
    Jedoch hat es mit

    C-Quellcode

    1. int32_t combined; // aus den vier Bytes ein Int32 machen
    2. combined = (long)msg[12] << 0 | (long)msg[11] << 8 | (long)msg[10] << 16 | (long)msg[9] << 24;


    bereits einwandfrei funktioniert.

    Nun suche ich die Entsprechung zum obigen C++ Code.
    Habe es so hinbekommen um den korrekten Wert zu bekomme:

    VB.NET-Quellcode

    1. Dim Wert As Double
    2. Dim by(4) As Byte
    3. by(0) = buffRecei(12)
    4. by(1) = buffRecei(11)
    5. by(2) = buffRecei(10)
    6. by(3) = buffRecei(9)
    7. Wert = BitConverter.ToInt32(by, 0)

    Liebe Grüße
    Roland Berghöfer

    Meine aktuellen und kostenlos verwendbaren Tools (mit VB.NET erstellt): freeremarkabletools.com | priconman.com | SimpleCalendar | AudibleTouch | BOComponent.com | bonit.at

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „dive26“ ()

    @BitBrösel

    Da muss ich Dir leider einmal widersprechen.

    Option Strict ist auf ON und die IDE meldet keinen Fehler.

    Der Wert ist ein vorzeichenbehafteter Wert ohne Kommastellen.
    Könnte da auch long verwenden, aber brauche dann eh für die Weiterverarbeitung Double.

    Bitconverter.ToSingle ergibt leider "NaN"

    VB.NET-Quellcode

    1. Dim Wert As Double
    2. Dim by(4) As Byte
    3. by(0) = buffRecei(9)
    4. by(1) = buffRecei(10)
    5. by(2) = buffRecei(11)
    6. by(3) = buffRecei(12)
    7. Wert = BitConverter.ToInt32(by, 0)
    8. DebugAusgabe(vbCrLf + "Alternatives Ergebnis: " + Wert.ToString, True)
    9. by(0) = buffRecei(12)
    10. by(1) = buffRecei(11)
    11. by(2) = buffRecei(10)
    12. by(3) = buffRecei(9)
    13. Dim SingleWert As Single = BitConverter.ToSingle(by, 0)
    14. DebugAusgabe(vbCrLf + "Alternatives Ergebnis 1: " + SingleWert.ToString, True)
    15. Wert = BitConverter.ToInt32(by, 0)
    16. Return Wert


    Das Ergebnis siehst im Screenshot.
    Das korrekte Ergebnis kommt auch mit BitConverter.ToInt32 raus.
    Bilder
    • 02122022160316.jpg

      81,48 kB, 995×231, 37 mal angesehen
    Liebe Grüße
    Roland Berghöfer

    Meine aktuellen und kostenlos verwendbaren Tools (mit VB.NET erstellt): freeremarkabletools.com | priconman.com | SimpleCalendar | AudibleTouch | BOComponent.com | bonit.at
    Das wundert mich jetzt aber, bei mir wird es auch nicht bemängelt, das sollte aber bemängelt werden, immerhin droht Daten-Verlust/Verfälschung.(Weil gerundet wird und eine Ganzzahl draus gemacht wird)

    Wenn da "Not A Number" rauskommt, dreh die Reihenfolge der Bytes mal um.
    Das z.B. gibt NaN und 0,00 aus:

    C#-Quellcode

    1. byte[] buffer = new byte[] { 0x0, 0x0, 0xFF, 0xFF };
    2. float d = BitConverter.ToSingle(buffer, 0);
    3. Console.WriteLine(d);
    4. d = BitConverter.ToSingle(buffer.Reverse().ToArray(), 0);
    5. Console.WriteLine(d.ToString("n2"));
    6. Console.Read();
    @BitBrösel
    Es kommt ja eh immer ein ganzzahliger Wert raus. Minus oder Plus. Das passt so. Muss dann nur noch durch 10, 100 der 1000 dividiert werden.
    Bitreihenfolge umdrehen ergibt einen ganz anderen (falschen) Wert.

    Es rechnet richtig, es gibt keinen Fehler. Mehr brauche ich eigentlich nicht ;)
    Liebe Grüße
    Roland Berghöfer

    Meine aktuellen und kostenlos verwendbaren Tools (mit VB.NET erstellt): freeremarkabletools.com | priconman.com | SimpleCalendar | AudibleTouch | BOComponent.com | bonit.at
    Gelöscht - falscher Post ;)
    Liebe Grüße
    Roland Berghöfer

    Meine aktuellen und kostenlos verwendbaren Tools (mit VB.NET erstellt): freeremarkabletools.com | priconman.com | SimpleCalendar | AudibleTouch | BOComponent.com | bonit.at

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „dive26“ ()