C - 将timeval转换为uint64_t

4

早上好!

我正在尝试从timeval结构中获取纳秒级时间戳(uint64_t)。

    struct timeval t;
    // initialized so that ....
    print("t sec: %ld, micro sec: %ld", t.tv_sec, t.tv_usec);
    // .... prints "t sec: 4, micro sec: 130728"

    long int sec = (long int) t.tv_sec;
    // just did that because I thought that using time_t in calculations may cause errors
    // print("%ld", sec) -> 4, so this seems to work as expected    

    uint64_t t_int = (sec * 1000000 + t.tv_usec) * 1000; // should be nanoseconds
    print("t_int (uint64_t) %llu", t_int);
    // prints "t_int (uint64_t) 18446744073545312320"

我的timevals应该绝对适合uint64_t。此外,从time_t转换为long int的强制转换有效,因此我所做的只是使用两个long int进行简单计算,将结果放入uint64_t中,所以我不确定为什么会得到明显错误的值。有什么想法吗?这只是格式化错误的一种错误方式(使用%llu)吗?

3
你的机器上long int的大小是多少?你得到的sec的值是多少?你是否意识到你的计算sec * 1000000是使用的long int而不是uint64_t?还有该代码片段中的所有其他计算都是如此。 - Gerhardh
1
使用正确的uint64_t格式化程序,将t_int打印为整数:printf("t_int (uint64_t) %"PRIu64,t_int);。请参见此处 - Gerhard
2个回答

3

两种选择:

uint64_t t_int = ((uint64_t)t.tv_sec * 1000000 + t.tv_usec) * 1000;

或者:

uint64_t t_int = (t.tv_sec * 1000000ULL + t.tv_usec) * 1000;

后一个跳过了类型转换,而是通过将*的右侧操作数已经是至少64位的类型(而不是将左侧向上转换)来执行提升。两种方法都可以;这只是个人风格的问题。
不要像你所做的那样使用long类型的临时变量。 long可能是32位类型,将无法覆盖2038年1月之后的时间。如果需要临时变量,请使用time_tint64_t或类似类型。但是没有必要使用临时变量。
另外,您可能需要考虑使用int64_t(并将ULL更改为LL),而不是uint64_t,以便表示1970年以前的时间。

考虑使用 int64_t 是个好主意,因为 指定.tv_usec.tv_sec 明确是有符号量。 - chux - Reinstate Monica
谢谢,运行得非常顺利。不过我会保持未签名状态,因为我只是用它来计算从开始时间到结束时间的时间。 - Noel93

2

您正在使用 long int 进行计算。因为它会溢出,导致未定义的行为,并将一些负值赋给 uint64_t,从而得到一个大的正值(在您的架构上)。请使用 uint64_tunsigned long long 精度进行计算。严谨起见,请对所有值进行强制转换:

uint64_t t_int = ((uint64_t)sec * 1000000ull + (uint64_t)t.tv_usec) * 1000ull; 

但由于促销活动,很可能只需要将一个值转换为uint64_t

uint64_t t_int = ((uint64_t)sec * 1000000 + t.tv_usec) * 1000;

3
一开始就存储long int sec = (long int) t.tv_sec;就是一个错误,对于32位长的实现,它将在Y2038年溢出。相反,您应该直接使用t.tv_sec,或者如果您坚持要使用无用的临时变量sec,则应将其类型设置为time_t - R.. GitHub STOP HELPING ICE

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接