Arithmetischer Überlauf ATmega328

Ich habe die folgende Funktion geschrieben, um den aktuellen Zeitstempel (seit 00 Uhr 1. Januar 1900) zu berechnen. Die Ausgabe, die ich bekomme, ist:

Current Time & Date : 20:5:32 25/7/2014<\r>
2014 7 25 20 5 32<\r>
retval 1 3597523200<\r>
retval 2 3597523200<\r>
retval 3 3613161600<\r>
retval 4 3615235200<\r>
retval 5 3615241664<\r>
retval 6 3615241964<\r>
retval 7 3615241996<\r>
retval 8 3615241996<\r>
Current time timestamp 3615241996

Grundsätzlich drucke ich den retval (Funktionsrückgabewert) bei jedem Schritt, um die Berechnungen zu überprüfen und zu verifizieren.

Die Berechnung stimmt bis retval 4(wenn die Stunden des aktuellen Tages in Sekunden umgewandelt werden). Ich denke, es könnte an einem arithmetischen Überlauf liegen, bin mir aber nicht sicher.

Das Problem scheint in dieser Zeile zu passieren:

retval += (d.hr * 60 * 60); printf("retval 5 %"PRIu32"\r", retval);

retval ist uint32_t und d.hr ist uint16_t

uint32_t DS1307_GET_CURRENT_TIMESTAMP()
{
    uint32_t retval = SECONDS_SINCE_1900_TO_2014;
    ds1307 d;
    DS1307_GET_DATETIME(&d);
    printf("%u %u %u %u %u %u\r", d.yy,d.mm,d.dd,d.hr, d.min, d.sec);
    printf("retval 1 %"PRIu32"\r", retval);
    //process complete years since 2014 to current year
    uint8_t i=0;
    for(i=0; i<(d.yy - 2014); i++)
    {
        if(DS1307_IS_LEAP_YEAR(2014+i)==0) retval += 31622400;
        else retval += 31536000;
    }
        printf("retval 2 %"PRIu32"\r", retval);

    //process complete months from beginning of current year to current date/time
    for(i=1; i<d.mm; i++)
    {
        if((i==1) || (i==3) || (i==5) || (i==7) || (i==8) || (i==10) || (i==12))
        {
            //31 days
            retval += (31 * 86400);
        }
        else if ((i==4) || (i==6) || (i==9) || (i==11))
        {
            //30 days
            retval += (30 * 86400);
        }
        else 
        {
            //i==2==february. check if leap year
            if(DS1307_IS_LEAP_YEAR(d.yy)==0) retval += (29 * 86400);
            else retval += (28 * 86400);
        }
    }
        printf("retval 3 %"PRIu32"\r", retval);

    //process complete days from beginning of month till current date
    retval += ((d.dd-1) * 86400);
    printf("retval 4 %"PRIu32"\r", retval);

    //process hours, min and seconds - CALCULATION DEVIATES HERE.
    retval += (d.hr * 60 * 60); printf("retval 5 %"PRIu32"\r", retval);
    retval += (d.min * 60); printf("retval 6 %"PRIu32"\r", retval);
    retval += d.sec; printf("retval 7 %"PRIu32"\r", retval);

        printf("retval 8 %"PRIu32"\r", retval);

    return retval;
}

Antworten (2)

20 * 60 * 60 = 72000

Sie müssen die Multiplikation in 32 Bit durchführen, um einen Überlauf zu vermeiden.

retval += (d.hr * 60UL * 60);
OK. Wenn ich also eine arithmetische Anweisung schreibe (Multiplikation in diesem Zusammenhang), wie entscheidet c, was der Überlaufpunkt wäre oder wie groß diese Operation maximal ist? Hängt es von der Größe der Speicherregister ab, die die Plattform bereitstellt (16 Bit im AVR-Fall)?
Es hängt von der Größe von ab, intes sei denn, einer der Operanden überschreibt es, daher das LSuffix für das Literal (für long).
Jetzt, wo ich darüber nachdenke, gibt es keinen Grund, es zu unterschreiben...

(3615235200-3613161600)/(24*60*60) = 24, also ist die Änderung bei 'retval 4' korrekt und kein Überlauf, also ist Ihre Behauptung, dass sie korrekt sind, "bis Sie zu retval 4 kommen", falsch.

retval 5 ergibt jedoch (3615241664-3615235200) = 60*60*20 - 2**16, also gibt es einen Überlauf. Verwenden Sie ein uint32_t auf der rechten Seite von retval += d.hr * uint32_t(60 * 60u).

Vor Ihrer Bearbeitung fehlte ein Leerzeichen zwischen "retval 3" und "3613161600" .

Das Leerzeichen nach retval 3 ist ein Tippfehler. Das ziehe ich in Erwägung. Wie bereits erwähnt, ist Retval 4 korrekt. Es ist Retval 5, das das falsche Ergebnis liefert. Schließlich wird der Code, wie in der Ausgabe gezeigt, am 25. Juli 2014 ausgeführt