Search code examples
ccastingarithmetic-expressionsint32uint32

Conversion to float in uint32_t calculation


I am trying to improve a SWRTC by modifying the definition of a second (long unsigned n_ticks_per_second) by synchronizing time with a server.

#include <stdint.h>
#include <stdio.h>

int main(int argc, char * argv[]){

    int32_t total_drift_SEC;
    int32_t drift_per_sec_TICK;
    uint32_t at_update_posix_time = 1491265740;
    uint32_t posix_time = 1491265680;
    uint32_t last_update_posix_time = 1491251330;
    long unsigned n_ticks_per_sec = 1000;

    total_drift_SEC = (posix_time - at_update_posix_time);
    drift_per_sec_TICK = ((float) total_drift_SEC) / (at_update_posix_time - last_update_posix_time);

    n_ticks_per_sec += drift_per_sec_TICK;

    printf("Total drift sec %d\r\n", total_drift_SEC);
    printf("Drift per sec in ticks %d\r\n", drift_per_sec_TICK);
    printf("n_ticks_per_second %lu\r\n", n_ticks_per_sec);

    return 0;

}

What I don't understand is that I need to cast total_drift_SEC to float in order to have a correct result in the end, ie to have n_ticks_per_sec equal to 1000 in the end.

The output of this code is:

Total drift sec -60

Drift per sec in ticks 0

n_ticks_per_second 1000

Whereas the output of the code without the cast to float is:

Total drift sec -60

Drift per sec in ticks 298054

n_ticks_per_second 299054


Solution

  • This line

    drift_per_sec_TICK = total_drift_SEC / (at_update_posix_time - last_update_posix_time);
    

    divides a 32 bit signed int by a 32 bit unsigned int.

    32 bit unsigned int has a higher rank then 32 bit signed int.

    When doing arithmetic operations the "Usual Arithmetic Conversions" are applied:

    From the C11 Standard (draft) 6.3.1.8/1:

    if the operand that has unsigned integer type has rank greater or equal to the rank of the type of the other operand, then the operand with signed integer type is converted to the type of the operand with unsigned integer type.

    So -60 gets converted to a (32 bit) unsigned int: 4294967236

    Here

    drift_per_sec_TICK = (float) total_drift_SEC / (at_update_posix_time - last_update_posix_time);
    

    The following applies (from the paragraph of the C Standard as above):

    if the corresponding real type of either operand is float, the other operand is converted, without change of type domain, to a type whose corresponding real type is float.


    To not blindly step into those traps always specify -Wconversion when compiling with GCC.