Let say we have a timestamp in nanoseconds and we need to substract 2.5 seconds from it. So I created code like that:
nanoseconds current_ts{1645546551805673592};
float diff = 2.5;
nanoseconds target_ts = duration_cast<nanoseconds>(current_ts - (seconds{1} * diff));
but instead of expected result (1645546549305673592
) it returns 1645546556876652544
. After some experiments I found out that the problem somehow relates to float
type, because if I change it to double it works fine. Also it works fine if I move current_ts
outside of duration_cast
Why that happens? Is it related to precision of float?
here is minimal example:
#include <chrono>
#include <iostream>
#include <list>
int main() {
using namespace std::chrono;
nanoseconds current_ts{1645546551805673592};
float context_pre_event_image_sec_ = 2.5;
nanoseconds target_ts = current_ts - duration_cast<nanoseconds>((seconds{1} * context_pre_event_image_sec_));
nanoseconds target_ts_2 = duration_cast<nanoseconds>(current_ts - (seconds{1} * context_pre_event_image_sec_));
std::cout << current_ts.count() << std::endl;
std::cout << target_ts.count() << std::endl;
std::cout << target_ts_2.count() << std::endl;
}
second output is expected and third is unexpected
An std::duration
object is parameterized by a Period
(represented as a fraction respective to seconds) and Rep
, an arbitrary numeric type.
The operator-
of std::duration
uses the std::common_type
helper template type to deduce both the best type for the Period (intuitively, the greatest common divisor) and Rep
.
For Rep
, this rule is used:
if
std::decay<decltype(false ? std::declval<T1>() : std::declval<T2>())>::type
is a valid type, the member type [of std::common_type] denotes that type
In your case T1 and T2 are int64_t
and float
, respectively. The type of the ternary operator is the result of applying implicit conversion to both types, which is again float
.
And, of course, a float cannot represent the expected result accurately. You can quickly verify this by plugging 1645546549305673592 into this handy IEEE-754 tool.