Reputation: 51255
Currently I am using boost::rational<std::uint64>
to keep track in my application.
Basically I have a clock that runs over a very long period of time and will be tick by different components of different time resolutions, e.g. 1/50s, 1/30s, 1001/30000s etc... I want to maintain perfect precision, i.e. no floating point. boost::rational
works well for this purpose, however I think it would be better design to use std::chrono::duration
for this.
My problem though is, how can I use std::chrono::duration
here? Since it uses a compile time period I don't quite see how I can use it in my scenario where I need to maintain precision?
Upvotes: 1
Views: 1389
Reputation: 218700
If I'm understanding your question, and if you know all of the different time resolutions at compile-time, then the following will do what you want. You can figure out the correct tick period by using common_type
on all of your different time resolutions as shown below:
#include <cstdint>
#include <chrono>
struct clock
{
typedef std::uint64_t rep;
typedef std::common_type
<
std::chrono::duration<rep, std::ratio<1, 50>>,
std::chrono::duration<rep, std::ratio<1, 30>>,
std::chrono::duration<rep, std::ratio<1001, 30000>>
>::type duration;
typedef duration::period period;
typedef std::chrono::time_point<clock> time_point;
static const bool is_steady = true;
static time_point now()
{
// just as an example
using namespace std::chrono;
return time_point(duration_cast<duration>(steady_clock::now().time_since_epoch()));
}
};
This will compute at compile-time the largest tick period which will exactly represent each of your specified resolutions. For example with this clock one can exactly represent:
The code below exercises this clock
and uses the "chrono_io" facility described here to print out not only the run-time number of ticks of your clock, but also the compile-time units of your clock-tick:
#include <iostream>
#include <thread>
#include "chrono_io"
int main()
{
auto t0 = clock::now();
std::this_thread::sleep_for(std::chrono::milliseconds(20));
auto t1 = clock::now();
std::cout << (t1-t0) << '\n';
}
For me this prints out:
633 [1/30000]seconds
Meaning: There were 633 clock ticks between calls to now()
and the unit of each tick is 1/30000 of a second. If you don't want to be beholden to "chrono_io" you can inspect the units of your clock with clock::period::num
and clock::period::den
.
If your different time resolutions are not compile-time information, then your current solution with boost::rational
is probably best.
Upvotes: 4
Reputation: 279215
You're allowed to set the period to 1
and use a floating point type for Rep
.
I suspect that you can do the same thing with boost::rational
, but you'll have to look quite closely at std::chrono
, which I haven't done. Look at treat_as_floating_point
and duration_values
. Also try to figure out what the standard means by "An arithmetic type or a class emulating an arithmetic type".
One might reasonably argue that if boost::rational
doesn't emulate an arithmetic type, then it's not doing its job. But it doesn't necessarily follow that it really does everything std::chrono::duration
expects.
Upvotes: 1