参考:
C++ 标准库 分数运算(ratio库)
再也不被时间束缚:C++ stdchrono时间库全面解析
C++11时间类
c++11 chrono全面解析(最高可达纳秒级别的精度)
C++ std::chrono库使用指南 (实现C++ 获取日期,时间戳,计时等功能)
一、std的分数ratio
在进行时间库之前,先看看std定义的分数,顺便,看看英文.....
template<intmax_t _Num, intmax_t _Den = 1>
struct ratio
{
static_assert(_Den != 0, "denominator cannot be zero");
static_assert(_Num >= -__INTMAX_MAX__ && _Den >= -__INTMAX_MAX__,
"out of range");
// Note: sign(N) * abs(N) == N
static constexpr intmax_t num =
_Num * __static_sign<_Den>::value / __static_gcd<_Num, _Den>::value;
static constexpr intmax_t den =
__static_abs<_Den>::value / __static_gcd<_Num, _Den>::value;
typedef ratio<num, den> type;
};
函数原型还是很朴实的,很容易理解,也就是说ratio包括3个成员,num、den和type,num表示分子,den表示分母,type是类型,而且是最简约分形式(说到这,想起一到leetcode题,大概意思就是,给两个数,以字符串输出相除的结果,如果是无限循环小数,比如1/3,就输出0.(3) 这种形式)。
1.1 ratio的函数
例如
type返回的一个是ratio<>,2/7+2/6=13/21
typedef std::ratio_add<std::ratio<2, 7>, std::ratio<2, 6>>::type test;
std::cout << test::num << "/" << test::den << std::endl;
std::cout << std::ratio_equal<std::ratio<1, 3>, std::ratio<1, 3>>::value << std::endl; // true
std::cout << std::ratio_equal<std::ratio<2, 3>, std::ratio<1, 3>>::value << std::endl; // false
std::cout << std::ratio_greater<std::ratio<2, 3>, std::ratio<1, 3>>::value << std::endl; // true
1.2 使用注意
- ratio定义时,其分母不能定义为0,否则编译不通过
- ratio定义时,如果不填分母,默认为1
- 如果分子无-号,分母有-号,分母的-号会被移动到分子身上
- 如果分子有-号,分母无-号,则分子的-号保持不动
- 如果分子分母都有-号,则-号都被消除
- 分子可以为0
- ratio会自动对分子和分母进行约分
例如
//无法编译通过,因为1/max乘以1/2会导致溢出,分母超过了其类型所能涵盖的极限
std::ratio_multiply<std::ratio<1, std::numeric_limits<long long>::max()>, std::ratio<1, 2>>::type;
// 表达式也无法通过编译,因为其除以了0:
typedef ratio<5, 3> FiveThirds;
typedef ratio<0> zero; //分子为0,分母默认为1
std::ratio_divide<FiveThirds, zero>::type;
// 但是下面的可以编译通过,因为下面只使用了ratio_divide<>,却没有实例化一个ratio<>对象,因此编译器检测不出来
typedef ratio<5, 3> FiveThirds;
typedef ratio<0> zero;
std::ratio_divide<FiveThirds, zero>; //只调用了ratio_divide,却没有实例化出任何ratio对象
1.3 stl的自定义类型
typedef ratio<1, 1000000000000000000LL> atto; // 微微微
typedef ratio<1, 1000000000000000LL> femto; // 飞秒
typedef ratio<1, 1000000000000LL> pico; // 皮秒
typedef ratio<1, 1000000000> nano; // 纳秒
typedef ratio<1, 1000000> micro; // 微秒
typedef ratio<1, 1000> milli; // 毫秒
typedef ratio<1, 100> centi; // centi:百分之一
typedef ratio<1, 10> deci; // deci:十分之一
typedef ratio<10, 1> deca; // deci:十倍
typedef ratio<100, 1> hecto; // hecto:百倍
typedef ratio<1000, 1> kilo; // 千
typedef ratio<1000000, 1> mega; // 兆
typedef ratio<1000000000, 1> giga; // 吉
typedef ratio<1000000000000LL, 1> tera; // 太
typedef ratio<1000000000000000LL, 1> peta; // 拍
typedef ratio<1000000000000000000LL, 1> exa; // 百亿亿
二、chrono库
chrono库三大组件:时间段(duration)、时间点(time_point)和时钟(clock)。
2.1 duration
类模板原型是
template<class _Rep,class _Period>
class duration
其中,_Rep可以理解为数值,Period可以理解为数值单位。例如
std::chrono::duration<int, std::ratio<1, 1>> ts(2)
表示数值是整形,单位是s,也就是表示2s,再例如
std::chrono::duration<float, std::ratio<1, 1>> ts(2.2)
表示数值是float,单位是秒,也就是2.2s
如果想调整数值,可以使用int,float,double。如果想改变单位,就是一切以std::ratio<1, 1>(也就是std::ratio<1>)为基准,比如
std::chrono::duration<float, std::ratio<10, 1>> ts(2.2)
// 等价std::chrono::duration<float, std::deca> ts(2.2)
表示2.2个10s,就是22秒,前面标准库定义了许多比率,可以直接拿来使用。再例如
std::chrono::duration<float, std::ratio<1, 10>> ts(2.2)
// 等价std::chrono::duration<float, std::deci> ts(2.2)
表示2.2个0.1s,也就是0.22s。自然,时间表述有时、分、秒、毫秒、微妙等等,时间chrono定义了这些概念
typedef duration<long long, nano> nanoseconds;
typedef duration<long long, micro> microseconds;
typedef duration<long long, milli> milliseconds;
typedef duration<long long> seconds;
typedef duration<long long, ratio<60>> minutes;
typedef duration<long long, ratio<3600>> hours;
因此上面的0.22s可以表示为
std::chrono::seconds ts(0.22);
// 或者std::chrono::milliseconds ts(220);
2.1.1 操作方法
duration构造的方法包括默认构造,拷贝构造和传递数值构造,例如
std::chrono::duration<int, std::milli> duration0; // 默认构造
std::chrono::duration<int, std::milli> duration1(500); // 传递数值
std::chrono::duration<double, std::ratio<1, 100>> duration2 = duration1; // 传递duration对象(即使单位不同也性)
// duration0:0 duration1:500 duration2:50
chrono提供了对duration加减乘除以及比较的方法,例如
std::chrono::duration<int, std::milli> duration1(500);
std::chrono::duration<double, std::nano> duration2(1.5);
std::chrono::duration<float, std::micro> duration3 = duration1 + duration2; // 表示501.5微秒
// std::chrono::duration<int, std::micro> duration3 = duration1 + duration2; // 错误
数值类型int可以转换成double或float,但是double和float不能转换成int,但是double和float两者可以互相转换。
再例如乘除:
std::chrono::duration<int, std::milli> duration4 = duration1 * 2; // 表示1000毫秒
std::chrono::duration<double, std::milli> duration5 = duration2 / 2; // 表示0.75毫秒
duration还可以做逻辑比较(<、<=、>、>=、==、!=)例如
std::chrono::duration<int, std::milli> duration1(500);
std::chrono::duration<double, std::nano> duration2(1.5);
bool result = duration1 > duration2; // 返回true,因为500毫秒大于1.5纳秒
duration还提供了单位转换函数duration_cast,例如
std::chrono::hours hour_time = std::chrono::hours(1);
std::chrono::minutes minutes_time = std::chrono::duration_cast<std::chrono::minutes>(hour_time);
std::chrono::seconds seconds_time = std::chrono::duration_cast<std::chrono::seconds>(hour_time);
std::chrono::milliseconds milliseconds_time = std::chrono::duration_cast<std::chrono::milliseconds>(hour_time);
std::chrono::microseconds microseconds_time = std::chrono::duration_cast<std::chrono::microseconds>(hour_time);
也就是类型转换函数。
2.2 clock
时间点就是某时刻的时间点,而想要获得时间点,肯定需要时钟,而chrono提供了3个时钟:system_clock、steady_clock和high_resolution_clock
2.2.1 system_clock
函数原型如下:
struct system_clock{
typedef chrono::nanoseconds duration;
typedef duration::rep rep;
typedef duration::period period;
typedef chrono::time_point<system_clock, duration> time_point;
static_assert(system_clock::duration::min()
< system_clock::duration::zero(),
"a clock's minimum duration cannot be less than its epoch");
static constexpr bool is_steady = false;
static time_point now() noexcept;
// Map to C API
static std::time_t to_time_t(const time_point& __t) noexcept{
return std::time_t(duration_cast<chrono::seconds>
(__t.time_since_epoch()).count());
}
static time_point from_time_t(std::time_t __t) noexcept
{
typedef chrono::time_point<system_clock, seconds> __from;
return time_point_cast<system_clock::duration>(__from(chrono::seconds(__t)));
}
};
可见system_clock的精度是纳秒,提供了now函数、to_time_t和from_time_t函数接口。
2.2.2 steady_clock
struct steady_clock{
typedef chrono::nanoseconds duration;
typedef duration::rep rep;
typedef duration::period period;
typedef chrono::time_point<steady_clock, duration> time_point;
static constexpr bool is_steady = true;
static time_point now() noexcept;
};
steady_clock的精度是纳秒,只提供了now接口。
2.2.3 high_resolution_clock
using high_resolution_clock = system_clock;
high_resolution_clock就是system_clock。
2.2.4 总结
auto tp0 = std::chrono::system_clock::now();
auto tp1 = std::chrono::steady_clock::now();
auto tp2 = std::chrono::high_resolution_clock::now();
std::cout << tp0.time_since_epoch().count() << std::endl;
std::cout << tp1.time_since_epoch().count() << std::endl;
std::cout << tp2.time_since_epoch().count() << std::endl;
上述输出结果,在我的电脑上 system_clock和high_resolution_clock是相同的,都是从1970年1月1日0时0分0秒开始的,以纳秒为单位的结果。而steady_clock则是从上次开机到现在递增增加的时间结果(这里我的电脑是这样的,ubuntu系统可以通过last reboot或uptime指令查看上次开机时间,但是这两个数值可能不一样,只有一个是准确的),单位时纳秒。
- system_clock:(1)系统级别的时钟,它表示实时时钟,也就是指示当前时间的时钟。它的时间点是与系统的时钟相关联的,可能受到时钟调整和时区的影响,或者网络自动调整时间。(2)获取当前的系统时间,可以用来进行日常时间计算和显示。它通常被用作默认的时钟类型。
- steady_clock:是一个单调递增的时钟,不受任何时钟调整或时区的影响。它提供了一个稳定、可靠的时间基准,适合用于测量时间间隔和计算算法的执行时间。
- high_resolution_clock:是一个可用于测量小时间间隔的时钟。它通常使用最高分辨率的时钟源来提供更高的时间精度。在大部分平台上,high_resolution_clock是steady_clock的别名,因此也是一个单调递增的时钟。
2.3 time_point
结构体原型:
template<typename _Clock, typename _Dur>
struct time_point
{
typedef _Clock clock;
typedef _Dur duration;
typedef typename duration::rep rep;
typedef typename duration::period period;
constexpr time_point() : __d(duration::zero())
{ }
constexpr explicit time_point(const duration& __dur)
: __d(__dur)
{ }
// conversions
template<typename _Dur2, typename = _Require<is_convertible<_Dur2, _Dur>>>
constexpr time_point(const time_point<clock, _Dur2>& __t)
: __d(__t.time_since_epoch())
{ }
// observer
constexpr duration time_since_epoch() const
{ return __d; }
// arithmetic
_GLIBCXX17_CONSTEXPR time_point& operator+=(const duration& __dur)
{
__d += __dur;
return *this;
}
_GLIBCXX17_CONSTEXPR time_point& operator-=(const duration& __dur)
{
__d -= __dur;
return *this;
}
// special values
static constexpr time_point min() noexcept
{ return time_point(duration::min()); }
static constexpr time_point max() noexcept
{ return time_point(duration::max()); }
private:
duration __d;
};
time_point则是根据不同的时钟获取时钟的时间点。例如
system_clock::time_point now = system_clock::now();
system_clock::time_point specific_time = system_clock::time_point(seconds(100000));
system_clock::time_point specific_time(seconds(100000));
std::cout << now_sys.time_since_epoch().count() << std::endl;
std::cout << specific_time0.time_since_epoch().count() << std::endl;
std::cout << specific_time1.time_since_epoch().count() << std::endl;
输出
1721640151178784335
100000000000000
100000000000000