在 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 int
:4294967236
这里
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
。