From 791eec063d54db116ca908230b8187e4cb40e3ed Mon Sep 17 00:00:00 2001 From: Giles314 Date: Sun, 4 Aug 2024 15:55:10 +0200 Subject: [PATCH] [#71] Fix rounding issues that make the tests fail --- pv/util.cpp | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/pv/util.cpp b/pv/util.cpp index dfb8c72b..24c724bb 100644 --- a/pv/util.cpp +++ b/pv/util.cpp @@ -199,11 +199,21 @@ static QString pad_number(unsigned int number, int length) QString format_time_minutes(const Timestamp& t, signed precision, bool sign) { - const Timestamp whole_seconds = floor(abs(t)); - const Timestamp days = floor(whole_seconds / (60 * 60 * 24)); - const unsigned int hours = fmod(whole_seconds / (60 * 60), 24).convert_to(); - const unsigned int minutes = fmod(whole_seconds / 60, 60).convert_to(); - const unsigned int seconds = fmod(whole_seconds, 60).convert_to(); + // Add the maximum representation error left by the last displayed digit + // so we can call floor for all computed values so that we never get 24 hours or 60 seconds + // but still avoiding adding half precision error : + // 0.9 for last digit will still be represented as 1 because 0.5 have been added before calling floor + const Timestamp abs_t_and_floor_margin = abs(t) + ldexp(Timestamp(0.1), std::max(0, precision)) * 0.5; + // Do integer computation it will be faster. Also limit number of divisions (and modulos). + // Note: unsigned longs (with at least 32 bits) can store at least 136 years of seconds. Should be enough... + const unsigned long whole_seconds = floor(abs_t_and_floor_margin).convert_to(); + const unsigned long days = std::floor(whole_seconds / (60 * 60 * 24)); + unsigned long remain_seconds = whole_seconds - days * (60 * 60 * 24); + // int (with at least 16 bits) are still sufficient for remaining parts + const unsigned int hours = (unsigned int)std::floor(remain_seconds / (60 * 60)); + remain_seconds -= hours * (60 * 60); + const unsigned int minutes = (unsigned int)std::floor(remain_seconds / 60); + const unsigned int seconds = (unsigned int)(remain_seconds - minutes * 60); QString s; QTextStream ts(&s); @@ -217,7 +227,8 @@ QString format_time_minutes(const Timestamp& t, signed precision, bool sign) // DD if (days) { - ts << days.str().c_str() << ":"; + ts << days << ":"; + ts << pad_number(hours, use_padding ? 2 : 0) << ":"; use_padding = true; } @@ -238,7 +249,7 @@ QString format_time_minutes(const Timestamp& t, signed precision, bool sign) if (precision) { ts << "."; - const Timestamp fraction = fabs(t) - whole_seconds; + const Timestamp fraction = abs(t) - floor(abs(t)); ostringstream ss; ss << fixed << setprecision(precision) << setfill('0') << fraction;