在时间::持续时间类型之间转换,其中两个分母的LCM都很大

Converting between chrono::duration types where the LCM of both denominators is large

本文关键字:两个 LCM 之间 类型 持续时间 时间 转换      更新时间:2023-10-16

不久前我遇到了一个问题,我必须在两种不同的持续时间类型之间转换,如下所示:

using MyType_T = duration<long long, ratio<1, 4294967296LL>>;
using OtherType_T = duration<long long, ratio<1, 10000000>>;

当我使用::boost::chrono::duration_cast<>时(或者std::chrono::duration_cast<>),从MyType_T到OtherType_T,反之亦然,我发现当源值超过某个限制时,这些类型的转换可能会产生错误的结果。

仅调用duration_cast(tm)存在问题的原因是,两个分母的LCM完全用56位表示。在这种情况下,转换是通过将tm中的值除以分母的LCM来完成的,并将其作为公共因子类型传递到OtherType_T持续时间的构造中。这将执行乘法运算以获得正确的值。

正如你所看到的,对于足够大的值(在这种情况下为64或更大),这意味着除法可以将所有值移到零,而你什么都得不到。

除了执行以下操作外,还有什么方法可以在不损失所有精度的情况下进行转换吗?

  1. 创建一个新类型Interim_T,它只是两者中的common_type,去掉了分母。typedef typename common_type<MyType_T, OtherType_T>::type CommonDuration;
  2. 对新类型的"From"值tm执行duration_cast,以获得一些值高位。auto highbits = duration_cast<duration<CommonDuration::rep, ratio<CommonDuration::num>>>(tm);
  3. 从原始值tm中减去高位,以获得值低位。auto lowbits = tm - highbits;
  4. 对于高位,添加duration_cast的结果(低位)。这就是结果。auto result = highbits + duration_cast<OtherType_T>(lowbits);

我会使用浮点类型作为临时类型:

using MyType_T = duration<long long, ratio<1, 4294967296LL>>;
using OtherType_T = duration<long long, ratio<1, 10000000>>;
using OtherType_F = duration<long double, OtherType_T::period>;
MyType_T x{0x7FFFFFFFFFFF};
auto y = duration_cast<OtherType_T>(OtherType_F{x});

语法更直观,逻辑也不那么复杂。请注意,如果结果不能容纳在OtherType_T中,则仍然会溢出。