在 uint32_t 计算中转换为浮点数

Conversion to float in uint32_t calculation

我正在尝试通过与服务器同步时间来修改秒的定义(long unsigned n_ticks_per_second)来改进 SWRTC。

#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;

}

我不明白的是,我需要将 total_drift_SEC 转换为 float 才能获得正确的结果最后,即 n_ticks_per_sec 最后等于 1000

这段代码的输出是:

Total drift sec -60

Drift per sec in ticks 0

n_ticks_per_second 1000

而没有强制转换为 float 的代码的输出是:

Total drift sec -60

Drift per sec in ticks 298054

n_ticks_per_second 299054

因为 "integer" 版本 total_drift_SEC 将变成 unsigned 所以 -60 --> 4294967236

4294967236 / 14410 = 298054

除法将使用浮点数计算:

-60/14410 = 0

参考第 53 页的 c-standard

6.3.1.8 Usual arithmetic conversions

1 Many operators that expect operands of arithmetic type cause conversions and yield result types in a similar way. The purpose is to determine a common real type for the operands and result. For the specified operands, each operand is converted, without change of type domain, to a type whose corresponding real type is the common real type. Unless explicitly stated otherwise, the common real type is also the corresponding real type of the result, whose type domain is the type domain of the operands if they are the same, and complex otherwise. This pattern is called the usual arithmetic conversions: [...] Otherwise, the integer promotions are performed on both operands. Then the following rules are applied to the promoted operands:

  • If both operands have the same type, then no further conversion is needed. Otherwise, if both operands have signed integer types or both have unsigned integer types, the operand with the type of lesser integer conversion rank is converted to the type of the operand with greater rank.
  • Otherwise, 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.
  • Otherwise, if the type of the operand with signed integer type can represent all of the values of the type of the operand with unsigned integer type, then the operand with unsigned integer type is converted to the type of the operand with signed integer type.
  • Otherwise, both operands are converted to the unsigned integer type corresponding to the type of the operand with signed integer type.

我的重点

这一行

drift_per_sec_TICK = total_drift_SEC / (at_update_posix_time - last_update_posix_time);

将 32 位 signed int 除以 32 位 unsigned int

32 位 unsigned int 的等级高于 32 位 signed int

进行算术运算时应用“普通算术转换”:

来自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.

因此 -60 被转换为(32 位)unsigned int4294967236

这里

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

以下内容适用(来自上述 C 标准的段落):

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.


为了不盲目地踏入这些陷阱,在使用 GCC 编译时总是指定 -Wconversion