Skip to content

Commit e804bff

Browse files
authored
Merge pull request #483 from visitorckw/introduce-mult-frac
Enhance precision and safety in time calculations
2 parents 26a5ef3 + 85ab051 commit e804bff

File tree

1 file changed

+15
-3
lines changed

1 file changed

+15
-3
lines changed

src/utils.c

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,19 @@
2626

2727
#define MAX_PATH_LEN 1024
2828

29+
/* Calculate "x * n / d" without unnecessary overflow or loss of precision.
30+
*
31+
* Reference:
32+
* https://elixir.bootlin.com/linux/v6.10.7/source/include/linux/math.h#L121
33+
*/
34+
static inline uint64_t mult_frac(uint64_t x, uint64_t n, uint64_t d)
35+
{
36+
const uint64_t q = x / d;
37+
const uint64_t r = x % d;
38+
39+
return q * n + r * n / d;
40+
}
41+
2942
static void get_time_info(int32_t *tv_sec, int32_t *tv_nsec)
3043
{
3144
#if defined(HAVE_POSIX_TIMER)
@@ -40,14 +53,13 @@ static void get_time_info(int32_t *tv_sec, int32_t *tv_nsec)
4053
*/
4154
if (info.denom == 0)
4255
(void) mach_timebase_info(&info);
43-
/* Hope that the multiplication doesn't overflow. */
44-
uint64_t nsecs = mach_absolute_time() * info.numer / info.denom;
56+
uint64_t nsecs = mult_frac(mach_absolute_time(), info.numer, info.denom);
4557
*tv_sec = nsecs / 1e9;
4658
*tv_nsec = nsecs - (*tv_sec * 1e9);
4759
#else /* low resolution timer */
4860
clock_t t = clock();
4961
*tv_sec = t / CLOCKS_PER_SEC;
50-
*tv_nsec = (t % CLOCKS_PER_SEC) * (1e9 / CLOCKS_PER_SEC);
62+
*tv_nsec = mult_frac(t % CLOCKS_PER_SEC, 1e9, CLOCKS_PER_SEC);
5163
#endif
5264
}
5365

0 commit comments

Comments
 (0)