From 83185abd17aa1080bebde159bc12a2fec75e9b07 Mon Sep 17 00:00:00 2001 From: Kuan-Wei Chiu Date: Sun, 1 Sep 2024 00:52:26 +0800 Subject: [PATCH 1/2] Add mult_frac() to handle fractional multiplication safely Introduce a new inline function mult_frac() to compute "x * n / d" with enhanced precision and safety. This function addresses the common issues of overflow and loss of precision that can occur with straightforward approaches to this calculation. Directly computing "x * n / d" can lead to problems: - Performing the division first can result in loss of precision due to integer truncation. - Performing multiplication before division can risk overflow. The mult_frac() function mitigates these issues by: 1. Calculating the quotient and remainder of 'x' divided by 'd'. 2. Using these intermediate results to perform the final calculation, thus avoiding intermediate overflow and preserving precision. This approach is based on the Linux kernel's mult_frac() macro [1], which follows a similar method to handle these calculations robustly. Link: https://elixir.bootlin.com/linux/v6.10.7/source/include/linux/math.h#L121 [1] --- utils.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/utils.c b/utils.c index b88170b..c91d40d 100644 --- a/utils.c +++ b/utils.c @@ -19,6 +19,19 @@ #endif #endif +/* Calculate "x * n / d" without unnecessary overflow or loss of precision. + * + * Reference: + * https://elixir.bootlin.com/linux/v6.10.7/source/include/linux/math.h#L121 + */ +static inline uint64_t mult_frac(uint64_t x, uint64_t n, uint64_t d) +{ + const uint64_t q = x / d; + const uint64_t r = x % d; + + return q * n + r * n / d; +} + void semu_timer_init(semu_timer_t *timer, uint64_t freq) { timer->freq = freq; From 35e2206e6c6f00e229954e9f1d9790814eda7654 Mon Sep 17 00:00:00 2001 From: Kuan-Wei Chiu Date: Sun, 1 Sep 2024 01:02:58 +0800 Subject: [PATCH 2/2] Update semu_timer_clocksource() to use mult_frac() Enhance the semu_timer_clocksource() function by utilizing the mult_frac() function to improve the precision of converting time to clock ticks. This update refines the calculations for converting time derived from clock_gettime() and mach_absolute_time() into clock ticks. This ensures that the time-to-clock-ticks conversion is more reliable and precise, addressing potential precision issues effectively. --- utils.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/utils.c b/utils.c index c91d40d..99b5d54 100644 --- a/utils.c +++ b/utils.c @@ -43,12 +43,12 @@ static uint64_t semu_timer_clocksource(uint64_t freq) #if defined(HAVE_POSIX_TIMER) struct timespec t; clock_gettime(CLOCKID, &t); - return (t.tv_sec * freq) + (t.tv_nsec * freq / 1e9); + return t.tv_sec * freq + mult_frac(t.tv_nsec, freq, 1e9); #elif defined(HAVE_MACH_TIMER) static mach_timebase_info_data_t t; if (mach_clk.denom == 0) (void) mach_timebase_info(&t); - return mach_absolute_time() * freq / t.denom * t.numer; + return mult_frac(mach_absolute_time() * freq, t.numer, t.denom); #else return time(0) * freq; #endif