#include #include #include #include #include #include #include #include #include #include #include #include static uint64_t iters; static uint64_t old_overflowed; static uint64_t new_overflowed; static uint64_t inherent_overflow; #define FMT64_N 24 static char * sigsafe_fmt_num(char fmtnum[static FMT64_N], uint64_t num) { size_t i; memset(fmtnum, 0, FMT64_N); for (i = FMT64_N - 2; i > 0; i--) { fmtnum[i] = '0' + (num % 10); num /= 10; if (num == 0) break; } return (&fmtnum[i]); } static void siginfo_handler(int dummy __unused) { char fmtnum[FMT64_N], *iters_str, fmtoldo[FMT64_N], *oldo_str, fmtnewo[FMT64_N], *newo_str, fmtinher[FMT64_N], *inher_str, fmtpctoof[FMT64_N], fmtpctnof[FMT64_N], fmtpctnofi[FMT64_N], *pctoof, *pctnof, *pctnofi; uint64_t iters_copy, oldcopy, newcopy, incopy; iters_copy = *(volatile uint64_t *)&iters; oldcopy = *(volatile uint64_t *)&old_overflowed; newcopy = *(volatile uint64_t *)&new_overflowed; incopy = *(volatile uint64_t *)&inherent_overflow; if (iters_copy == 0) return; unsigned pof, pnof, pnofi; pof = (100 * oldcopy) / iters_copy; pnof = (100 * newcopy) / iters_copy; if (newcopy == 0) pnofi = 100; else pnofi = (100 * incopy) / newcopy; iters_str = sigsafe_fmt_num(fmtnum, iters_copy); oldo_str = sigsafe_fmt_num(fmtoldo, oldcopy); newo_str = sigsafe_fmt_num(fmtnewo, newcopy); inher_str = sigsafe_fmt_num(fmtinher, incopy); pctoof = sigsafe_fmt_num(fmtpctoof, pof); pctnof = sigsafe_fmt_num(fmtpctnof, pnof); pctnofi = sigsafe_fmt_num(fmtpctnofi, pnofi); #define TEXT1 " examples, " #define TEXT2 " (" #define TEXT3 "%) old OF, " #define TEXT4 TEXT2 #define TEXT5 "% tot) new OF, " #define TEXT6 " inherent truncation (" #define TEXT7 "% new OF)\n" #define WRITE_STRING(t) (void)write(STDERR_FILENO, t, strlen(t)) // "%llu examples, %llu (%d%%) old OF, %llu (%d%% tot) new OF, %llu // inherent truncation (%d%% new OF)" WRITE_STRING(iters_str); WRITE_STRING(TEXT1); WRITE_STRING(oldo_str); WRITE_STRING(TEXT2); WRITE_STRING(pctoof); WRITE_STRING(TEXT3); WRITE_STRING(newo_str); WRITE_STRING(TEXT4); WRITE_STRING(pctnof); WRITE_STRING(TEXT5); WRITE_STRING(inher_str); WRITE_STRING(TEXT6); WRITE_STRING(pctnofi); WRITE_STRING(TEXT7); #undef WRITE_STRING #undef TEXT1 #undef TEXT2 #undef TEXT3 #undef TEXT4 #undef TEXT5 #undef TEXT6 #undef TEXT7 } static uint64_t cute_nonoverflow_div(uint64_t a, uint64_t b, uint64_t c) { return ((a/c)*b + (a%c)*(b/c) + (a%c)*(b%c)/c); } static __int128_t verify_div(uint64_t a, uint64_t b, uint64_t c) { __int128_t wa, wb, wc; wa = a; wb = b; wc = c; return ((wa * wb) / wc); } static uint64_t overflow_div(uint64_t a, uint64_t b, uint64_t c) { return ((a * b) / c); } int main(void) { struct sigaction sa; uint64_t a, b, c; int rc; sa = (struct sigaction){ .sa_handler = siginfo_handler }; rc = sigaction(SIGINFO, &sa, NULL); if (rc != 0) err(1, "sigaction"); for (;; iters++) { arc4random_buf(&a, sizeof(a)); arc4random_buf(&b, sizeof(b)); /* * a = "total time in useconds" * b = count of user or system ticks * c = count of total ticks * * therefore * * b <= c * a < ~10^4 years, or 10^10 * seconds_per_year, or about 10^18 ~= 2^60. * c typically <= a (e.g., 1000 hz => a is c/1000; 100 hz => a is * c/10000). */ a %= (1ull << 60); if (a == 0) a = 1; c = a / 1000; if (c == 0) c = 1; b %= c; if (b == 0) b = 1; uint64_t old, cute; __int128_t verify; old = overflow_div(a,b,c); cute = cute_nonoverflow_div(a,b,c); verify = verify_div(a,b,c); if (verify != old) old_overflowed++; if (verify != cute) new_overflowed++; if (verify > UINT64_MAX) inherent_overflow++; } return 0; }