Rechenfehler im Arduino bei Linksshift 8 ?

Heute ist Stammtischzeit:
Jeden Donnerstag 20:30 Uhr hier im Chat.
Wer Lust hat, kann sich gerne beteiligen. ;)
  • Hallo, Freunde das Arduino..

    Ich bin auf einen merkwürdigen Rechenfehler gestossen beim Schreiben von Long-Werten auf den internen EEPROM des Arduino mini pro:

    Code-Beschreibung:
    Die Laufvariablen a,b,c,d stellen die byte-Werte im EEPROM dar (das Reinschreiben ist richtig).
    Um daraus wieder einen long-Wert zu machen, werden diese "stellenrichtig" nach links geschoben, wie man das halt so macht.
    Ich habe nun die Byte Werte durch 4 Schleifen simuliert, damit jeder den Fehler ggf. bei sich zu Hause nachstellen kann...

    Fehler:
    Dabei tritt immer dann ein Rechenfehler auf, wenn eine 8-fache Linksverschiebung eines Wertes gemacht wird, wo das höchste Bit gesetzt ist (alle Werte >=128):
    Die Verschiebung führt zu dem unten im 2. Codefenster zu sehenden Ergebnis...

    Vollständiger Testcode:

    Ergebnis des Testlaufes:

    Code
    c=124 = 7C00
    c=125 = 7D00
    c=126 = 7E00
    c=127 = 7F00
    c=128 = FFFF8000

    3 Möglichkeiten:
    * Mein Code enthält einen Fehler (?)
    * Der Compiler setzt den Code falsch um (Version 1.0.5-r2)
    * Der Arduino rechnet intern falsch (Schiebeoperationen)

    Was denkt ihr?

    Kann diesen Fehler jemand auf seinem Arduino nachstellen?

    :helpnew:

    /Edit:
    Gleiches Fehlerbild bei Verwendung der "nightly run" Compliler-Version von gestern... :s

    /edit2
    Ich habe den Code gekürzt, durchlauf nur wenige Sekunden, gleiches Bild

    Ich habe den Fehler bei Github eingestellt... mal sehen.

  • Nope ...
    das wird mitnichten so gemacht ;) ...

    Das Ergebnis - also der long Wert wird geshiftet, nicht die Bytes.
    Ich erinnere mich auch noch an Assembler, da war das wohl so, dass wenn das höchstwertige Bit gesetzt ist, und die Datenbreite für die shift-Operation nicht ausreicht, das entsprechende Register links mit 1en aufgefüllt wird.
    Zudem widerstrebt mir, dass Du byte statt unsigned char verwendest ... ich meine mich zu erinnern, dass sas so ein Makro war und ebenfalls signed ist.
    Ich hab' das jetzt mal so:

    Spoiler anzeigen


    #include <SoftwareSerial.h>

    void setup()
    {
    Serial.begin(57600);
    unsigned long l = 0;
    unsigned char a, b, c, d;

    for (a=0;a<0xff;a++) {
    for (b=0;b<0xff;b++) {
    for (byte c=0;c<0xff;c++) {
    for (byte d=0; d<0xff;d++) {
    Serial.print("a="); Serial.print(a, HEX);
    Serial.print(", b="); Serial.print(b, HEX);
    Serial.print(", c="); Serial.print(c, HEX);
    Serial.print(", d="); Serial.print(d, HEX); Serial.print(" = ");
    l = 0;
    l |= a; // << 24;
    l = l << 24 | b; // << 16;
    l = l << 16 | c; // << 8;
    l = l << 8 | d;

    Serial.println(l, HEX);
    if (l > 0xffffff) delay(100000);
    }
    }
    }
    }
    }

    void loop() {}


    probiert und habe das Gefühl, dass es funktioniert ;) ...

    cheerio,
    -ds-

    Nachtrag:
    Sakera ... ab dem Moment, in dem der Wert von b ungleich 0 ist, kommt er wieder in's stolpern ... :denker:
    Tja ... keine Ahnung, wo das Hund begraben liegt. Das wird sich aber rausfinden lassen.
    Vielleicht gibt's nen workaround, mal sehen.

    cheers,
    -ds-

  • Hm, (Denkfehler bei mir?) :wallbash:

    Schau ich mir nachher an, sry... hab schon alles aus... und mein Bett schreit nach mir :mad_GREEN:

    Ich habe die Konvertierung aus:
    http://rclermont.blogspot.de/2010/02/conver…rray-in-cc.html

    und da die Sequenz

    Code
    unsigned long int anotherLongInt;
    
    
      anotherLongInt = ( (byteArray[0] << 24) 
                       + (byteArray[1] << 16) 
                       + (byteArray[2] << 8) 
                       + (byteArray[3] ) );

    uminterpretiert...

    bis nachher ;)
    ... und Danke für dein Mühe ! :danke_ATDE:

  • Jo hey ...

    vertrackste Geschichte diese ....
    Nix zu danken übrigens. Solche Dinge reizen mich einfach. Liegt vielleicht daran, dass ich viel in der QS für SINIX und so manches SDH Board von Lucent unterwegs war.
    Aber ich habe folgenden Workaround, der funktioniert:

    Spoiler anzeigen


    #include <SoftwareSerial.h>


    void setup()
    {
    Serial.begin(57600);
    unsigned long l = 0;
    unsigned int sx1, sx2;
    unsigned char a, b, c, d;
    char pbuffer[30];
    unsigned char LongConvert[4];



    Serial.print("LONG is "); Serial.println(sizeof(l));

    for (a=0x01;a != 0xff;a++) {
    for (b=0x01;b != 0xff;b++) {
    for (c=0x01;c != 0xff;c++) {
    for (d=0x01; d != 0xff;d++) {
    Serial.print("a="); Serial.print(a, HEX);
    Serial.print(", b="); Serial.print(b, HEX);
    Serial.print(", c="); Serial.print(c, HEX);
    Serial.print(", d="); Serial.print(d, HEX);

    LongConvert[3] = a;
    LongConvert[2] = b;
    LongConvert[1] = c;
    LongConvert[0] = d;

    l = * ((unsigned long*) &LongConvert[0]);

    // l = 0;
    // sx1 = 0;
    // sx2 = 0;
    // l = a << 24 | b << 16 | c << 8 | d;
    // l |= l << 24 | a; // << 24;
    // l = l << 16 | b; // << 16;
    // l = l << 8 | c; // << 8;
    // l = l | d;

    // Serial.print(" : "); Serial.print(l, HEX);
    // l = (l >> 24) | a;
    // Serial.print(" : "); Serial.print(l, HEX);
    // l = l | (l >> 16) | b;
    // Serial.print(" : "); Serial.print(l, HEX);
    // l = l | (l >> 8) | c;
    // Serial.print(" : "); Serial.print(l, HEX);
    // l = l | d;
    // Serial.print(" : "); Serial.print(l, HEX);

    sprintf(pbuffer, " = %08lx\n", l);
    Serial.print(" pbuffer -> "); Serial.print( pbuffer );

    Serial.print(" matches l -> "); Serial.println( l, HEX );

    // sx1 = (sx1 >> 8) + a;
    // sx1 += b;
    // sx2 = (sx2 >> 8) + c;
    // sx2 += d;
    // Serial.print( "sx1 -> " );
    // Serial.print(sx1, HEX);
    // Serial.print( " sx2 -> " );
    // Serial.println(sx2, HEX);
    // l = ((l >> 16) | sx1) + sx2;
    // Serial.print(l, HEX);
    // sprintf(pbuffer, " = %08x\n", l);
    // Serial.print( pbuffer );
    Serial.println("------------------");

    // if (l > 0xffffff) delay(100000);
    }
    }
    }
    }


    }

    void loop() {}

    Sonderbarerweise habe ich auch per google bis jetzt nichts finden können, dass dies ein issue darstellt resp. dass der Arduino das nicht kann.
    Ich hab's jetzt mal auf einem Pro Mini probiert.
    Jetzt probier ich das noch auf einem MEGA, der momentan eh nix zu tun hat.
    so long,
    -ds-

    Nachtrag:
    Also auf dem MEGA ist es das gleiche Verhalten. Sonderbar, sonderbar ...
    Aber: der workaround funktioniert auch auf dem MEGA :)

  • So, eine Antwort auf Github und die Lösung ist da: War mein Fehler :wallbash:

    @ds: hattest Recht: :thumbs1: der Compiler schnallt zwar, dass die Variable nach der Verschiebung dann einem long zugewiesen wird, die zusätzlichen Bits werden aber mit "1" aufgefüllt...

    Oh man, ich hab nun schon seit 20 Jahren mit C++ zu tun (jedoch bisher das Shiften kaum verwendete und wenn dann nur auf long) - auf sowas muss man erstmal kommen :wallbash:

    Die Lösung besteht also aus einem "vorauseilendem" cast vor dem Shiften und alles geht gut ..

    Code
    l = (unsigned long)c <<  8;

  • Danke für's bescheid sagen.
    Allerdings: Im nachherein bestrachtet gefällt mir die Möglichkeit mit dem workaround fast besser (uchar-Arry mit 4 Byte, Zeiger darauf auf long* casten) ...

    kein Ding :)

    Dein Workaround läuft letztlich auf das Gleiche hinaus...
    Das "Problem" ist m.E. dieses kranke Auffüllen der höheren Bits mit '1' ... auch wenn das nun seit Urzeiten eine Konvention zu sein scheint, erschliesst sich mir der tiefere Sinn nicht...

    Naja, die Umsetzung läuft jedenfalls... :thumbs1:

    OT:
    Heute den 1. Testdurchlauf mit dem Fototimer gemacht: lol... die Stromaufnahme ist so gering, dass die Powerbank, die das Teil per USB versorgen sollte, nach 3 min. wieder abgeschaltet hat, weil die Last (Arduino) zu niedrig war :lol:

  • Hallöle Zentris,

    wie jetzt ... schaltet Deine Powerbank die Akkus ab? Echt? Das hab' ich alerdings noch nie gehört ( was aber nix heissen muss ;) ). Naja, macht vielleicht auch Sinn beim Laden oder so ...

    Das linksbündige Auffüllen mit 1er macht ist nicht so ein Quatsch wie es vielleicht aussieht. Auf Assembler-Ebene, also in den Registern der CPU, sind bei negativen Werten linksbündig alle Stellen, mindestens aber das höchstwertige Bit, auf 1 gesetzt. Shiftest Du nach rechts, muss mit 1er aufgefüllt werden, weil sonst das Vorzeichen verloren geht ;) ...

    Na dann viel Erfolg noch,
    -ds-

  • Als Powerbank habe ich die hier [Anzeige]...

    Das Abschalten macht schon Sinn: Wenn der angeschlossene Verbraucher/Handy keinen Strom mehr zieht /voll ist, dann schaltet der verbaute Schaltregler ab und das Teil geht "schlafen"... Ein Tastendruck - und es geht wieder an - wird ja auch so gestartet...

    Auf diese Weise wird fast kein Strom verbraucht, wenn nix dranhängt... sollten alle Powerbänke so machen, denke ich. :thumbs1:

    Jo, auf Assemblerebene... hm ja... ist ja ok: aber ich wollte doch "unsigned"... och man... :lol:

    Die Zeit, wo ich 'nen x86 und 'nen C64 in Assembler verarzten musste, liegen so ca. 30 Jahre zurück :D
    Damals hab ich das noch verstanden, heute bin ich da viel zu weit wech... trau ich mich nicht mehr ran... Wenn ich mir die Befehlsvielfalt der heutigen CPUs ansehe... =(

    ;)

  • Ah ja, stimmt - macht Sinn - ist aber in diesem Fall blöd :s . Dass das jede Powerbank unterstützt wäre schon gut, aber ich denke dann müsste man dieses Feature abschalten können. Jedenfalls danke für die Info ...

    Nun, das mit dem "professionellen" programmieren in Assembler liegt bei mir auch gut 30 Jahre zurück ... ( ui, zwei so alte Knochen ;) ).
    Ich habe mir aber, nur aus Neugier, in den letzten Jahren immer wieder mal das eine oder andere Experimentierboard zugelegt und damit ein wenig "rumgespielt".
    Eine Weile waren das PICs, dann aber ausschliesslich ATMEL-MCUs. Also ich finde den AVR-Assembler noch einigermassen überschaubar (ok, abhängig vom Modell).
    Da fällt mir ein: ich habe da für Linux eine, wie ich finde, wirklich geniale Entwicklungsumgebung für AVRs entdeckt: LunaAVR. Kennst Du das schon?

    Bis demnächst im selben Theater,
    -ds-

Jetzt mitmachen!

Du hast noch kein Benutzerkonto auf unserer Seite? Registriere dich kostenlos und nimm an unserer Community teil!