【问题标题】:64-bit operations sometime fail64 位操作有时会失败
【发布时间】:2018-05-02 18:52:24
【问题描述】:

这是我第一次使用 64 位 time_t 结构来做时间运算。编译器是 TI 针对 CC3220SF 的 C 编译器。

这是我的函数,应该将秒数返回到下一个事件:

#define ACQ_INTERVAL    1 // in minutes
time_t _lastAcquisition;

time_t _timeToAcquire(void)
{
    time_t now = _getEpoch(); // from internal RTC
    if (_lastAcquisition == 0) _lastAcquisition = now;
    time_t diff = now - _lastAcquisition; // seconds since last acquisition
    time_t next = ACQ_INTERVAL * 60;

    UART_PRINT("Interval is %lld s\r\n", next);
    UART_PRINT("Current epoch is %lld, last acquisition was %lld, next one in %lld s\r\n", now, _lastAcquisition, next - diff);
    return next - diff; // seconds to next acquisition
}

UART_PRINT 只是 printf 的包装器。 这里是_getEpoch() 函数:

time_t _getEpoch(void)
{
    _i16 ret;
    _u8 pConfigOpt = SL_DEVICE_GENERAL_DATE_TIME;
    _u16 pConfigLen = sizeof(SlDateTime_t);

    SlDateTime_t dateTime = {0};
    ret = sl_DeviceGet(SL_DEVICE_GENERAL, &pConfigOpt, &pConfigLen, (unsigned char *) &dateTime);
    ASSERT_ON_ERROR(ret);

    struct tm t;
    time_t t_of_day;

    t.tm_year = dateTime.tm_year - 1900;
    t.tm_mon = dateTime.tm_mon - 1;
    t.tm_mday = dateTime.tm_day;
    t.tm_hour = dateTime.tm_hour;
    t.tm_min = dateTime.tm_min;
    t.tm_sec = dateTime.tm_sec;
    t.tm_isdst = -1;
    t_of_day = mktime(&t);

    return t_of_day;
}

通常它工作正常,但有时我会得到一些奇怪的输出:

间隔为 60 秒

当前epoch是1511088032,最后一次采集是1511086500,下一次采集是18446744073709550144秒

显然有问题。差值应该是 1532。 此外,这个巨大的数字看起来像是 2^64 - x。但是 x 应该是 1742 ......我不明白它可能来自哪里。

我仔细检查了time_t的定义:

typedef long long __time64_t;

#if defined(_TARGET_DEFAULTS_TO_TIME64) || (__TI_TIME_USES_64) && __TI_TIME_USES_64)
    typedef __time64_t time_t;
#else
    typedef __time32_t time_t;
#endif

__TI_TIME_USES_64 已定义并设置为 1(IDE 向我显示#if 的第一部分已启用)。

【问题讨论】:

  • 你能保证函数mktime()支持64位吗?听起来好像发生了 32 位环绕。 (如果某处 64 位数字转换为 32 位,然后又转换回 64 位,由于 32 位中的上溢/下溢和符号扩展,您可能会得到令人惊讶的结果。)此外,如果 time_t 意外地是 32 位,而您 @987654332 @ 和 "%lld" 它考虑了 4 个额外的“随机”字节。为了消除疑虑,我会在适当的位置添加assert(sizeof (time_t) == 8);(如果assert 不可用,则打印出来)。
  • mktime() 定义如下:static __inline time_t mktime(struct tm *tptr) { return __mktime64(tptr); }
  • 您的lastAcquisition 变量或timeToAcquire() 函数是否被多个线程/任务同时调用?您确定您的代码不存在竞争条件或线程安全问题吗?
  • 我没有使用 RTOS,所以我只有一个线程。我不会从 ISR 调用任何东西。此外,两者都只被引用一次,lastAcquisition 在同一函数中调用 timeToAcquire() 之前设置了几行。
  • time_t 是某种算术类型。与其猜测它匹配long long,不如说清楚。 UART_PRINT("%lld\n", now, (long long) (next - diff)); 如果这不起作用,那么“UART_PRINT 只是 printf 的包装器。”为 false 或 printf() 不合规。

标签: c time-t


【解决方案1】:

你有

now = 1511088032
_lastAcquisition = 1511086500
diff = now - _lastAcquisition = 1532
next - diff = 60 - diff = -1472

看起来您平台上的 %lld 说明符有问题,并且像 %llu(无符号长长)一样工作。你看到的巨大数字是

18,446,744,073,709,550,144 = 2^64 - 1472

正好是 -1472 转换为无符号 64 位值。如果 long long 有 64 位,则 %lld 说明符永远不会产生大于 INT64_MAX 的数字,即 9,223,372,036,854,775,807。

【讨论】:

  • 嗯,我刚试过:time_t foo = -1000; UART_PRINT("%lld\r\n", foo);,输出是-1000。因此,该功能有效......但如果它被窃听有时不会......
  • @Mark:由于该示例中的foo 是一个编译时常量,因此编译器很可能已经将printf 调用替换为一个简单的puts 调用和一个常量字符串.我不了解您的编译器,但众所周知 GCC 会进行此类优化。在这种情况下,它仅仅意味着编译器做了正确的事情,而运行时printf 实现可能仍然存在错误。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-20
  • 1970-01-01
  • 2016-09-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多