From c0dfe26ef53a29a0cb44720f29f5a078a7b3cca2 Mon Sep 17 00:00:00 2001 From: Rick Christy Date: Fri, 23 Oct 2026 12:43:27 -0400 Subject: [PATCH] Allow TIMER accuracy below one millisecond TIMER(accuracy) always started from a whole number of milliseconds, so TIMER(.0001) or TIMER(.000001) could never be finer than 1 ms. That is too coarse for frame timing on high refresh rate displays (8.3 ms per frame at 120 Hz). The time is now read in microseconds. An accuracy finer than .001 uses it; TIMER, TIMER(.001) and coarser accuracies keep the exact whole millisecond value they returned before. Co-Authored-By: Claude Opus 5.5 --- internal/c/libqb/src/datetime.cpp | 23 ++++++++----- .../timer/timer_sub_millisecond.bas | 33 +++++++++++++++++++ .../timer/timer_sub_millisecond.output | 4 +++ 3 files changed, 51 insertions(+), 9 deletions(-) create mode 100644 tests/compile_tests/timer/timer_sub_millisecond.bas create mode 100644 tests/compile_tests/timer/timer_sub_millisecond.output diff --git a/internal/c/libqb/src/datetime.cpp b/internal/c/libqb/src/datetime.cpp index 705cce469c..008f3926e0 100644 --- a/internal/c/libqb/src/datetime.cpp +++ b/internal/c/libqb/src/datetime.cpp @@ -27,11 +27,11 @@ void Sleep(uint32_t milliseconds) { } #endif -static uint64_t millis_since_midnight() { +static uint64_t micros_since_midnight() { const auto current_time = std::chrono::system_clock::now(); - // Gives us the number of milliseconds past the current second - const auto millis_only = std::chrono::duration_cast(current_time.time_since_epoch()).count() % 1000; + // Gives us the number of microseconds past the current second + const auto micros_only = std::chrono::duration_cast(current_time.time_since_epoch()).count() % 1000000; // Convert to time_t and then hour/min/sec. localtime() takes the current timezone into account for us. const auto cur_ttime = std::chrono::system_clock::to_time_t(current_time); @@ -40,23 +40,28 @@ static uint64_t millis_since_midnight() { // Compute current time as number of seconds past midnight const auto seconds = local->tm_hour * 3600 + local->tm_min * 60 + local->tm_sec; - return static_cast(seconds * 1000) + static_cast(millis_only); + return static_cast(seconds) * 1000000 + static_cast(micros_only); } double func_timer(double accuracy, int32_t passed) { if (new_error) return 0; - auto result = millis_since_midnight() / 1000.0; + if (passed && accuracy <= 0.0) { + error(5); + return 0; + } + + const auto micros = micros_since_midnight(); + + // An accuracy finer than 1 ms (e.g. TIMER(.000001)) gets the microsecond count. + // Otherwise TIMER keeps using whole milliseconds, exactly as before. + auto result = (passed && accuracy < 0.001) ? micros / 1000000.0 : (micros / 1000) / 1000.0; // Default accuracy = 18.2 Hz if (!passed) { accuracy = 18.2; } else { - if (accuracy <= 0.0) { - error(5); - return 0; - } accuracy = 1.0 / accuracy; } diff --git a/tests/compile_tests/timer/timer_sub_millisecond.bas b/tests/compile_tests/timer/timer_sub_millisecond.bas new file mode 100644 index 0000000000..ddbc0e7e9f --- /dev/null +++ b/tests/compile_tests/timer/timer_sub_millisecond.bas @@ -0,0 +1,33 @@ +$Console:Only + +' TIMER with an accuracy finer than 1 ms returns microseconds; .001 and +' coarser keep returning whole milliseconds, as they always have. + +Dim As Double t, prev, msPart +Dim As Long i, offMs, backwards, notWholeMs + +' TIMER(.001) is always a whole number of milliseconds +For i = 1 To 2000 + t = Timer(.001) + msPart = t * 1000 - Int(t * 1000 + .5) + If Abs(msPart) > .000001 Then notWholeMs = notWholeMs + 1 +Next +If notWholeMs = 0 Then Print "Timer(.001) whole milliseconds: ok" Else Print "Timer(.001) not whole milliseconds:"; notWholeMs + +' TIMER(.000001) has digits below the millisecond, and never runs backwards +prev = Timer(.000001) +For i = 1 To 20000 + t = Timer(.000001) + msPart = t * 1000 - Int(t * 1000 + .5) + If Abs(msPart) > .0005 Then offMs = offMs + 1 + If t < prev And prev - t < 43200 Then backwards = backwards + 1 ' (ignores the midnight wrap) + prev = t +Next +If offMs > 0 Then Print "Timer(.000001) below a millisecond: ok" Else Print "Timer(.000001) only whole milliseconds" +If backwards = 0 Then Print "Timer(.000001) never backwards: ok" Else Print "Timer(.000001) went backwards:"; backwards + +' Both agree to within a couple of milliseconds +t = Abs(Timer(.000001) - Timer(.001)) +If t < .002 Or t > 86399 Then Print "Timer(.000001) agrees with Timer(.001): ok" Else Print "Timer(.000001) differs from Timer(.001) by"; t + +System diff --git a/tests/compile_tests/timer/timer_sub_millisecond.output b/tests/compile_tests/timer/timer_sub_millisecond.output new file mode 100644 index 0000000000..2bf2c594b9 --- /dev/null +++ b/tests/compile_tests/timer/timer_sub_millisecond.output @@ -0,0 +1,4 @@ +Timer(.001) whole milliseconds: ok +Timer(.000001) below a millisecond: ok +Timer(.000001) never backwards: ok +Timer(.000001) agrees with Timer(.001): ok