Desbordamiento aritmético ATmega328

He escrito la siguiente función para calcular la marca de tiempo actual (desde las 00 h del 1 de enero de 1900). La salida que obtengo es:

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

Básicamente estoy imprimiendo el retval (valor de retorno de la función) en cada paso para comprobar y verificar los cálculos.

El cálculo coincide hasta retval 4(cuando las horas del día actual se convierten en segundos). Creo que podría deberse a un desbordamiento aritmético, pero no estoy seguro.

El problema parece estar sucediendo en esta línea:

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

retval es uint32_t y d.hr es 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;
}

Respuestas (2)

20 * 60 * 60 = 72000

Debe realizar la multiplicación en 32 bits para evitar un desbordamiento.

retval += (d.hr * 60UL * 60);
De acuerdo. Entonces, cuando escribo una declaración aritmética ac (multiplicación en este contexto), ¿cómo decide c cuál sería el punto de desbordamiento o cuál es el tamaño máximo de esa operación? ¿Depende del tamaño de los registros de almacenamiento que proporciona la plataforma (16 bits en el caso de AVR)?
Depende del tamaño de inta menos que uno de los operandos lo anule, de ahí el Lsufijo en el literal (para long).
Ahora que lo pienso, no hay razón para que se firme...

(3615235200-3613161600)/(24*60*60) = 24, por lo que el cambio en 'retval 4' es correcto y no un desbordamiento, por lo que su afirmación de que son correctos "hasta que llegue a retval 4" es falsa.

retval 5 sin embargo da (3615241664-3615235200) = 60*60*20 - 2**16, por lo que hay un desbordamiento. Use un uint32_t en el lado derecho de retval += d.hr * uint32_t(60 * 60u).

Antes de su edición, le faltaba un espacio entre "retval 3" y "3613161600" .

El espacio después de retval 3 es un error tipográfico. Lo estoy tomando en consideración. También como se mencionó, retval 4 es correcto. Es retval 5 que está dando el resultado incorrecto. Por último, como se muestra en la salida, el código se ejecuta el 25 de julio de 2014