lib: timeutil: fix conversion drift

Fix conversion drifts for large deltas by only applying float
operations when the skew requires it. This helps because not all
integers are representable as floats, so large integers are
neccessarily quantised when performing float operations.

When required, floating-point operations are now performed on doubles
instead of floats.

Fixes #37263.

Signed-off-by: Jordan Yates <jordan.yates@data61.csiro.au>
This commit is contained in:
Jordan Yates 2021-07-28 16:52:41 +10:00 committed by Christopher Friedt
commit 2c1f184d02

View file

@ -28,8 +28,8 @@
* @see http://howardhinnant.github.io/date_algorithms.html#days_from_civil * @see http://howardhinnant.github.io/date_algorithms.html#days_from_civil
*/ */
static int64_t time_days_from_civil(int64_t y, static int64_t time_days_from_civil(int64_t y,
unsigned int m, unsigned int m,
unsigned int d) unsigned int d)
{ {
y -= m <= 2; y -= m <= 2;
@ -134,8 +134,13 @@ int timeutil_sync_ref_from_local(const struct timeutil_sync_state *tsp,
if ((tsp->skew > 0) && (tsp->base.ref > 0) && (refp != NULL)) { if ((tsp->skew > 0) && (tsp->base.ref > 0) && (refp != NULL)) {
const struct timeutil_sync_config *cfg = tsp->cfg; const struct timeutil_sync_config *cfg = tsp->cfg;
int64_t local_delta = local - tsp->base.local; int64_t local_delta = local - tsp->base.local;
int64_t ref_delta = (int64_t)(tsp->skew * local_delta) * /* (x * 1.0) != x for large values of x.
cfg->ref_Hz / cfg->local_Hz; * Therefore only apply the multiplication if the skew is not one.
*/
if (tsp->skew != 1.0) {
local_delta *= (double)tsp->skew;
}
int64_t ref_delta = local_delta * cfg->ref_Hz / cfg->local_Hz;
int64_t ref_abs = (int64_t)tsp->base.ref + ref_delta; int64_t ref_abs = (int64_t)tsp->base.ref + ref_delta;
if (ref_abs < 0) { if (ref_abs < 0) {
@ -157,10 +162,16 @@ int timeutil_sync_local_from_ref(const struct timeutil_sync_state *tsp,
if ((tsp->skew > 0) && (tsp->base.ref > 0) && (localp != NULL)) { if ((tsp->skew > 0) && (tsp->base.ref > 0) && (localp != NULL)) {
const struct timeutil_sync_config *cfg = tsp->cfg; const struct timeutil_sync_config *cfg = tsp->cfg;
int64_t ref_delta = (int64_t)(ref - tsp->base.ref); int64_t ref_delta = (int64_t)(ref - tsp->base.ref);
double local_delta = (ref_delta * cfg->local_Hz) / cfg->ref_Hz /* (x / 1.0) != x for large values of x.
/ tsp->skew; * Therefore only apply the division if the skew is not one.
*/
int64_t local_delta = (ref_delta * cfg->local_Hz) / cfg->ref_Hz;
if (tsp->skew != 1.0) {
local_delta /= (double)tsp->skew;
}
int64_t local_abs = (int64_t)tsp->base.local int64_t local_abs = (int64_t)tsp->base.local
+ (int64_t)local_delta; + (int64_t)local_delta;
*localp = local_abs; *localp = local_abs;
rv = (int)(tsp->skew != 1.0); rv = (int)(tsp->skew != 1.0);