整数绕行不是很好理解

integer wrap-around not really understood

本文关键字:很好 整数      更新时间:2023-10-16

我有以下一段代码,它被称为,大约每秒 100次

if (elapsed_centiseconds_mod_60000 < 59999) {
    ++elapsed_centiseconds_mod_60000;
} else {
    elapsed_centiseconds_mod_60000 = 0;
}
if (elapsed_centiseconds_mod_60000 % 6000 == 0) {
    ++elapsed_minutes;
}

变量和常量的类型

volatile uint16_t elapsed_centiseconds_mod_60000;
volatile uint8_t start_minute_mod_10;
const int8_t calibration_second = 5;

那么恰好每秒一次,下面的语句被称为

int16_t get_phase_deviation(uint8_t current_second, uint8_t current_minute_mod_10) {
    int32_t deviation=
         (int32_t) elapsed_centiseconds_mod_60000 -
        ((int32_t) current_second        + (int32_t)600 - (int32_t)calibration_second) * (int32_t) 100 -
        ((int32_t) current_minute_mod_10 + (int32_t) 60 - (int32_t)start_minute_mod_10)* (int32_t)6000;
    deviation %= 60000;
    return (int16_t)(deviation<=30000? deviation: deviation-60000);
}

代码的思想是检测两个振荡器的相对频率误差。然而,代码并没有像预期的那样工作。它将开始输出一个0的偏差。但是,一旦经过的厘秒比它们应该的多一厘秒,它将立即跳转到5537的输出,这与2^ 16% 60000 + 1相吻合。我试图将中间值转换为int32_t,但问题仍然存在。通常在100秒或10000厘秒后出现问题。我怀疑有一些绕圈子的问题,但我看不出来。

有谁知道是哪个词引起的,为什么?

您错误地将get_phase_deviation()的返回值转换为deviation %= 60000的范围是-59999 to 59999 (而不是 0 to 59999)。你应该改变这一行:

 return (int16_t)(deviation<=30000? deviation: deviation-60000);

正确处理偏差<= -30000视情况而定,或者将返回类型更改为int32_t

查看具体案例来理解你得到的数字,假设:

 calibration_second = 0;
 start_minute_mod_10 = 0;

那么对于一个完美的系统,get_phase_deviation()返回:

 deviation = -420000; // As computed
 deviation %= 60000;  // == 0
 return 0;

如果您领先1厘秒,则变为:

 deviation = -419999; // As computed
 deviation %= 60000;  // == -59999
 return (int16_t) (-59999);  // == 5537