- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
我正在编写一些必须处理 NTP 时间的代码。我决定使用 std::chrono::duration
来代表他们,我希望这会让我的时间数学更容易做。
NTP 时间由无符号的 64 位整数表示,高 32 位为纪元以来的秒数,低 32 位为小数秒。纪元日期是 1900 年 1 月 1 日,但这与我在这里处理的问题不同,我已经知道如何处理它。对于我正在使用的示例,假设时间为 1970 年 1 月 1 日,以使数学更简单。
持续时间表示很简单:std::chrono::duration<std::uint64_t, std::ratio<1, INTMAX_C(0x100000000)>>
.问题出在我尝试在 NTP 时间和系统时钟之间转换时。
在我的系统上,std::chrono::system_clock::duration
是std::chrono::duration<int64_t, std::nano>
.当我尝试使用 std::chrono::duration_cast
在这两个持续时间之间转换,我在任一方向上都被截断了。在调试器中对此进行跟踪,我发现它与 duration_cast
有关。实现,在 libstdc++、libc++ 和 boost::chrono 中以同样的方式失败。简而言之,要从系统表示转换为 ntp 表示,它乘以 8388608/1953125 的比率,并在另一个方向上乘以相反的比率。它先乘以分子,再除以分母。数学是正确的,但初始乘法溢出 64 位表示并被截断,尽管实际转换(除法后)仍然很容易用这些位表示。
我可以手动进行此转换,但我的问题如下:
to_sys
和 from_sys
函数,我可以简单地使用 std::chrono::clock_cast
吗?而不必担心其他微妙的问题?这是我用来测试这个的代码和一些示例输出:
// #define BOOST
#ifdef BOOST
# include <boost/chrono.hpp>
# define PREFIX boost
#else
# include <chrono>
# define PREFIX std
#endif
#include <cstdint>
#include <iostream>
namespace chrono = PREFIX::chrono;
using PREFIX::ratio;
using PREFIX::nano;
using ntp_dur = chrono::duration<uint64_t, ratio<1, INTMAX_C(0x100000000)>>;
using std_dur = chrono::duration<int64_t, nano>;
int main() {
auto write = [](auto & label, auto & dur) {
std::cout << label << ": "
<< std::dec << dur.count()
<< std::hex << " (" << dur.count() << ")" << std::endl;
};
auto now = chrono::system_clock::now().time_since_epoch();
write("now", now);
std::cout << '\n';
std::cout << "Naive conversion to ntp and back\n";
auto a = chrono::duration_cast<std_dur>(now);
write("std", a);
auto b = chrono::duration_cast<ntp_dur>(a);
write("std -> ntp", b);
auto c = chrono::duration_cast<std_dur>(b);
write("ntp -> std", c);
std::cout << '\n';
std::cout << "Broken down conversion to ntp, naive back\n";
write("std", a);
auto d = chrono::duration_cast<chrono::seconds>(a);
write("std -> sec", d);
auto e = chrono::duration_cast<ntp_dur>(d);
write("sec -> ntp sec", e);
auto f = a - d;
write("std -> std frac", f);
auto g = chrono::duration_cast<ntp_dur>(f);
write("std frac -> ntp frac", f);
auto h = e + g;
write("ntp sec + ntp frac-> ntp", h);
auto i = chrono::duration_cast<std_dur>(h);
write("ntp -> std", i);
std::cout << '\n';
std::cout << "Broken down conversion to std from ntp\n";
write("ntp", h);
auto j = chrono::duration_cast<chrono::seconds>(h);
write("ntp -> sec", j);
auto k = chrono::duration_cast<std_dur>(j);
write("src -> std sec", j);
auto l = h - j;
write("ntp -> ntp frac", l);
auto m = chrono::duration_cast<std_dur>(l);
write("ntp frac -> std frac", m);
auto n = k + m;
write("std sec + std frac-> std", n);
}
示例输出:
now: 1530980834103467738 (153f22f506ab1eda)
Naive conversion to ntp and back
std: 1530980834103467738 (153f22f506ab1eda)
std -> ntp: 4519932809765 (41c60fd5225)
ntp -> std: 1052378865369 (f506ab1ed9)
Broken down conversion to ntp, naive back
std: 1530980834103467738 (153f22f506ab1eda)
std -> sec: 1530980834 (5b40e9e2)
sec -> ntp sec: 6575512612832804864 (5b40e9e200000000)
std -> std frac: 103467738 (62acada)
std frac -> ntp frac: 103467738 (62acada)
ntp sec + ntp frac-> ntp: 6575512613277195414 (5b40e9e21a7cdc96)
ntp -> std: 1052378865369 (f506ab1ed9)
Broken down conversion to std from ntp
ntp: 6575512613277195414 (5b40e9e21a7cdc96)
ntp -> sec: 1530980834 (5b40e9e2)
src -> std sec: 1530980834 (5b40e9e2)
ntp -> ntp frac: 444390550 (1a7cdc96)
ntp frac -> std frac: 103467737 (62acad9)
std sec + std frac-> std: 1530980834103467737 (153f22f506ab1ed9)
最佳答案
- Is this a bug in the implementations, or just a limitation?
只是一个限制。实现的行为符合规范。
- If a limitation, should I have realized this would be a problem? I didn't see any documentation that would seem to lead to being able to guess that this would not work.
规范在23.17.5.7 [time.duration.cast] C++ 标准。它记录了转换算法以按照您在问题中描述的方式运行。
- Is there a generic way to implement this that isn't prey to the same problem?
在处理非常精细的单位或非常大的范围时,您需要注意溢出错误的可能性。 chrono::duration_cast
旨在成为尽可能高效地处理最常见转换的最低级别工具。 chrono::duration_cast
尽可能准确,尽可能完全消除除法。
然而,没有任何一种转换算法能够始终使用有限的存储空间获得任意转换的正确答案。 C++17 引入了三种基于 duration_cast
的新转换算法并且旨在指导存在截断的方向:
floor // truncate towards negative infinity
ceil // truncate towards positive infinity
round // truncate towards nearest, to even on tie
您可以编写自己的通用转换函数来处理诸如您描述的困难情况。这种由客户提供的转换不太可能适合一般用途。例如:
template <class DOut, class Rep, class Period>
DOut
via_double(std::chrono::duration<Rep, Period> d)
{
using namespace std::chrono;
using dsec = duration<long double>;
return duration_cast<DOut>(dsec{d});
}
以上示例中客户端提供的转换从 duration<long double>
反弹.这不太容易溢出(尽管不能免疫),通常在计算上会更昂贵,并且会遇到精度问题(并且取决于 numeric_limits<long double>::digits
)。
对于我 ( numeric_limits<long double>::digits == 64
),输入 1530996658751420125ns
往返1530996658751420124ns
(关闭 1ns)。可以通过使用 round
改进该算法在 duration_cast
(再次以更多计算为代价):
template <class DOut, class Rep, class Period>
DOut
via_double(std::chrono::duration<Rep, Period> d)
{
using namespace std::chrono;
using dsec = duration<long double>;
return round<DOut>(dsec{d});
}
现在我的往返非常适合输入 1530996658751420125ns
.但是,如果您的 long double
只有 53 位精度,甚至没有 round
将提供完美的往返。
- Should this be reported, and to whom?
任何人都可以按照 instructions at this link 提交针对 C++ 标准库一半的缺陷报告.这样一份报告将由 C++ 标准委员会的 LWG 小组委员会审阅。它可能会被采取行动,或者可能被宣布为 NAD(不是缺陷)。如果一个问题包含建议的措辞(关于您希望如何更改规范的详细说明),它将有更高的成功机会。
即您认为标准应该怎么说?
- Finally, if, when C++20 comes around, I use an NTP clock with manually curated
to_sys
andfrom_sys
functions, will I simply be able to usestd::chrono::clock_cast
and not have to worry about other subtle problems?
找出答案的一种方法是用 this existing prototype 进行试验<chrono>
的 C++20 规范草案扩展名。
关于c++ - 比率乘法期间的 chrono::duration_cast 问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51225009/
我有一个问题,但由于 this question 部分正在解决,但我想知道如何计算给定间隔之间的天数。 这是一个计算员工休假天数的查询。所以给定(或不给定)一个日期范围,我想计算给定间隔之间有多少假期
变量dateSubtract结果是 16,但我想找到这 2 天之间的总天数,应该是 165。没有 JODA TIME 我该如何做到这一点? String date = "06/17/2014"; Da
我想选择创建日期介于给定月份的第一天和最后一天之间的记录。我通过以下方式计算开始日期和结束日期的月份: 日期“月份”只是时间范围内的随机日期 Calendar cal = Calendar.getIn
我有一个对你们大多数人来说可能微不足道的问题。我尝试了很多,没有找到解决方案,所以如果有人能给我提示,我会很高兴。起点是每周 xts -时间序列。 月周值(value)目标 2011 年 12 月 W
我有一个 Facebook 应用程序,它将用户生日作为 varchar 存储在 mysql 数据库中。我正在尝试获取所有用户的生日 1周后推出,如果是在本周如果生日是上周。 在我的 php 中,我获取
我正在使用以下代码来获取年、月、日中的两个日期之间的差异 tenAppDTO.getTAP_PROPOSED_START_DATE()=2009-11-01 tenAppDTO.getTAP_PRO
我想检查当前时间(在 C++ 中)是否在一个时间范围内。 我想从元组 ("12:00", "17:30") 构造时间范围,即 (string, string) 并检查时间 now() 是否介于两者之间
gitlab 有一个功能,如果我在提交消息中放入票号,那么提交将与 gitlab.com 上的票相关联。 这在进行代码审查时非常方便。不幸的是,开发人员有时会忘记这样做。 我想指定 git hooks
我正在尝试制作使用SQLite数据库的简单注册/登录应用程序,到目前为止我得到了这段代码。这是我的“注册” Activity ,我猜它应该在按下注册按钮后将用户名和 pin(密码)实现到数据库,遗憾的
我正在尝试打开、关闭和写入文件。每当我尝试打开一个文件时,如果我提供的路径中不存在该文件,程序就会告诉我。如果存在,程序将读取其中的内容并显示它。如果用户不想查找文件,可以选择创建文件并用数据填充它。
我想要我的至slideToggle每当发生 react 性变化时,但到目前为止我还无法使其发生。我尝试在 rendered 中使用 JQuery和created模板的事件,但它没有触发。 触发此操作的
我们的 MySQL 遇到了神秘的网络问题。简单的更新查询(使用索引更新单行)通常会立即运行,然后有时(假设 1000 次中有 1 次)因超时而失败。与简单的插入查询相同。数据库没有过载。我们怀疑网络问
我正在使用 actionbarsherlock 的 ActionBar,第一次以横向或水平方向运行应用程序时,选项卡以 Tabs Mode 显示。将方向更改为纵向后,导航模式仍在 Tabs 中。第二次
每天晚上(太平洋标准时间晚上 8 点)我都会对生产数据库(innoDB 引擎)进行全局备份。 这是 mysqldump 命令: mysqldump -u$MYSQLUSER -p$MYSQLPWD -
当我的应用程序第一次启动时,它应该显示用户协议(protocol),这是一个 59kb 的 txt 文件。由于读取文件并将其附加到 TextView 需要一些时间,因此我决定在异步任务中执行此操作并在
如何只允许一个“.”在按键期间的javascript中? 我这里有一个代码: function allowOneDot(txt) { if ((txt.value.split(".")
我已经创建了像主页和用户这样的标题图标。在桌面 View 中,如果我单击用户图像,它会显示相应的重定向页面。如果我在选项卡或移动 View 中将其最小化, 它什么都不显示。此问题仅发生在用户图像上,而
下面的代码在 Release模式下工作,并且仅在 Debug模式下在 g_ItemList.push_back() 引发错误,我浏览了一些 SO 帖子和论坛。有人提到 "You can't itera
我遇到了一个我似乎无法解决的 mmap 问题。下面是设置:我使用 malloc 将一个巨大的多维数组分配到内存中,用我的值填充它,然后我想将它保存在一个文件中。该数组包含 3200000000 个字节
尝试加载共享库: handle = dlopen( "libaaa.so.2.5", RTLD_NOW ); if ( !handle ) { printf("Failed t
我是一名优秀的程序员,十分优秀!