#include <iostream>
#include <chrono>
int main()
{
constexpr std::chrono::duration<float> kLastWarningMsgDuration{ 10 };
// default time point (zero)
std::chrono::steady_clock::time_point lastWarningMsgTime1;
// minimum time point (some negative value)
std::chrono::steady_clock::time_point lastWarningMsgTime2 = std::chrono::steady_clock::time_point::min();
// current time point
std::chrono::steady_clock::time_point lastWarningMsgTime3 = std::chrono::steady_clock::now();
bool result1 = std::chrono::steady_clock::now() - lastWarningMsgTime1 >= kLastWarningMsgDuration;
bool result2 = std::chrono::steady_clock::now() - lastWarningMsgTime2 >= kLastWarningMsgDuration;
bool result3 = std::chrono::steady_clock::now() - lastWarningMsgTime3 >= kLastWarningMsgDuration;
std::cout << result1 << std::endl; // true, more than 10 seconds
std::cout << result2 << std::endl; // false ???
std::cout << result3 << std::endl; // false, less than 10 seconds
}
I didn't dive too deep into this but I guess the problem is that the second result overflows over what the poor 32-bit float
can hold. I could live with this discovery however there's one thing which got stuck in my head and it is this presentantion's bold statement:
If you make a reasonable change that doesn't involve explicit type conversion syntax (and it compiles), you can have confidence that you have not introduced a bug.
Well, this confidence has been quite shaken for me now. Did I misunderstand what time_point::min()
does or that statement is not entirely true?
It's not float
that's overflowing (that'd be a very large number), std::chrono::steady_clock::now() - lastWarningMsgTime2
is taking some presumably smallish positive integer and subtracting the maximum sized negative integer. Assuming std::chrono::steady_clock
uses int64_t
you're essentially doing:
int64_t lastWarningMsgTime2 = std::numeric_limits<int64_t>::min();
int64_t now = 12345;
int64_t diff = now - lastWarningMsgTime2;
This obviously overflows, as int64_t
is signed this is undefined behaviour (gcc gives a large negative number as the result https://godbolt.org/z/rPYM6oj75).
Most std::chrono
implementations use signed 64-bit values in their durations, even if those durations are nanoseconds that gives you a range of nearly 600 years, therefore in normal usage the statement you've quoted would be correct. In normal usage subtracting two timestamps wouldn't overflow but yes there is no checking for overflows in std::chrono
.
There are obviously still pitfalls, subtracting time_point::min()
is one of those pitfalls but I can't see a reason why you'd normally want to do that.