diff --git a/src/components/ble/SimpleWeatherService.h b/src/components/ble/SimpleWeatherService.h index 469b571aea..4d09d66240 100644 --- a/src/components/ble/SimpleWeatherService.h +++ b/src/components/ble/SimpleWeatherService.h @@ -34,6 +34,7 @@ #include "components/datetime/DateTimeController.h" #include #include "displayapp/InfiniTimeTheme.h" +#include "utility/Math.h" int WeatherCallback(uint16_t connHandle, uint16_t attrHandle, struct ble_gatt_access_ctxt* ctxt, void* arg); @@ -77,11 +78,11 @@ namespace Pinetime { } [[nodiscard]] int16_t Celsius() const { - return (PreciseCelsius() + 50) / 100; + return Utility::RoundedDiv(PreciseCelsius(), static_cast(100)); } [[nodiscard]] int16_t Fahrenheit() const { - return (PreciseFahrenheit() + 50) / 100; + return Utility::RoundedDiv(PreciseFahrenheit(), static_cast(100)); } [[nodiscard]] lv_color_t Color() const { diff --git a/src/displayapp/DisplayApp.cpp b/src/displayapp/DisplayApp.cpp index 35330fb7f7..84fa603622 100644 --- a/src/displayapp/DisplayApp.cpp +++ b/src/displayapp/DisplayApp.cpp @@ -53,10 +53,13 @@ #include "displayapp/screens/settings/SettingBluetooth.h" #include "displayapp/screens/settings/SettingOTA.h" +#include "utility/Math.h" + #include "libs/lv_conf.h" #include "UserApps.h" #include +#include using namespace Pinetime::Applications; using namespace Pinetime::Applications::Display; @@ -167,18 +170,15 @@ TickType_t DisplayApp::CalculateSleepTime() { // Calculates how many system ticks DisplayApp should sleep before rendering the next AOD frame // Next frame time is frame count * refresh period (ms) * tick rate - auto RoundedDiv = [](uint32_t a, uint32_t b) { - return ((a + (b / 2)) / b); - }; - // RoundedDiv overflows when numerator + (denominator floordiv 2) > uint32 max - // in this case around 9 hours (=overflow frame count / always on refresh period) - constexpr TickType_t overflowFrameCount = (UINT32_MAX - (1000 / 16)) / ((configTICK_RATE_HZ / 8) * alwaysOnRefreshPeriod); + // Avoid overflow when numerator would be > uint32 max + // in this case around 18 hours (=overflow frame count / always on refresh period) + constexpr TickType_t overflowFrameCount = std::numeric_limits::max() / ((configTICK_RATE_HZ / 8) * alwaysOnRefreshPeriod); TickType_t ticksElapsed = xTaskGetTickCount() - alwaysOnStartTime; // Divide both the numerator and denominator by 8 (=GCD(1000,1024)) // to increase the number of ticks (frames) before the overflow tick is reached - TickType_t targetRenderTick = RoundedDiv((configTICK_RATE_HZ / 8) * alwaysOnFrameCount * alwaysOnRefreshPeriod, 1000 / 8); - + TickType_t targetRenderTick = + Utility::RoundedDiv((configTICK_RATE_HZ / 8) * alwaysOnFrameCount * alwaysOnRefreshPeriod, static_cast(1000 / 8)); // Assumptions // Tick rate is multiple of 8 @@ -188,7 +188,8 @@ TickType_t DisplayApp::CalculateSleepTime() { // Frame count must always wraparound more often than the system tick count does // Always on overflow time (ms) < system tick overflow time (ms) // Using 64bit ints here to avoid overflow - static_assert((uint64_t) overflowFrameCount * (uint64_t) alwaysOnRefreshPeriod < (uint64_t) UINT32_MAX * 1000ULL / configTICK_RATE_HZ); + static_assert((uint64_t) overflowFrameCount * (uint64_t) alwaysOnRefreshPeriod < + (uint64_t) std::numeric_limits::max() * 1000ULL / configTICK_RATE_HZ); if (alwaysOnFrameCount == overflowFrameCount) { alwaysOnFrameCount = 0; diff --git a/src/heartratetask/HeartRateTask.cpp b/src/heartratetask/HeartRateTask.cpp index e9bc11a30a..fa352772f7 100644 --- a/src/heartratetask/HeartRateTask.cpp +++ b/src/heartratetask/HeartRateTask.cpp @@ -3,16 +3,12 @@ #include #include +#include "utility/Math.h" + using namespace Pinetime::Applications; namespace { constexpr TickType_t backgroundMeasurementTimeLimit = 30 * configTICK_RATE_HZ; - - // dividend + (divisor / 2) must be less than the max T value - template - constexpr T RoundedDiv(T dividend, T divisor) { - return (dividend + (divisor / static_cast(2))) / divisor; - } } std::optional HeartRateTask::BackgroundMeasurementInterval() const { @@ -48,9 +44,9 @@ TickType_t HeartRateTask::CurrentTaskDelay() { static_cast((Pinetime::Controllers::Ppg::deltaTms)) < std::numeric_limits::max(), "Overflow"); - TickType_t elapsedTarget = RoundedDiv(static_cast(configTICK_RATE_HZ / 2) * (static_cast(count) + 1U) * - static_cast((Pinetime::Controllers::Ppg::deltaTms)), - static_cast(1000 / 2)); + TickType_t elapsedTarget = Utility::RoundedDiv(static_cast(configTICK_RATE_HZ / 2) * (static_cast(count) + 1U) * + static_cast((Pinetime::Controllers::Ppg::deltaTms)), + static_cast(1000 / 2)); // On count overflow, reset both count and start time // Count is 16bit to avoid overflow in elapsedTarget diff --git a/src/utility/Math.h b/src/utility/Math.h index e8d190c72d..314e4e373e 100644 --- a/src/utility/Math.h +++ b/src/utility/Math.h @@ -1,10 +1,33 @@ #pragma once #include +#include namespace Pinetime { namespace Utility { // returns the arcsin of `arg`. asin(-32767) = -90, asin(32767) = 90 int16_t Asin(int16_t arg); + + // Round half away from zero integer division + // If T signed, divisor cannot be std::numeric_limits::min() + // Adapted from https://github.com/lucianpls/rounding_integer_division + // Under the MIT license + template + constexpr T RoundedDiv(T dividend, T divisor) { + bool neg = divisor < 0; + if (neg) { + // overflows if divisor is minimum value for T + divisor = -divisor; + } + + T m = dividend % divisor; + T h = divisor / 2 + divisor % 2; + T res = (dividend / divisor) + (!(dividend < 0) & (m >= h)) - ((dividend < 0) & ((m + h) <= 0)); + + if (neg) { + res = -res; + } + return res; + } } }