Search code examples
floating-pointdoubleieee-754floating-accuracy

How much accuracy is lost due to representing time as a IEEE 754 binary64 number?


My python program has a busy loop. Within the loop, some delays are calculated in the following fashion:

 timeout_s: float = args.timeout_s
 timeout_end_s: float = time.time() + timeout_s
 while True:
     now: float = time.time()
     if now > timeout_end_s:
        break

The time.time() function returns a float which as for as I know is not an accurate way of storing numbers due to floating-point precision. The current time since epoch is 1710402835. float in python is an IEEE 754 binary64 on "most systems", it is safe to assume I am using only "most systems".

Assuming timeout_s is relatively small and human comprehensible, between 0.1 seconds and under 10 seconds, and current time is nowadays, how much at time.time() + timeout_s calculation accuracy is lost due to using IEEE 754 binary64 to represent the time?

When does the moment happen when floating point accuracy will become troubling? In other words, for how long will the calculation accuracy of time.time() + timeout_s be within 1 millisecond? In 100 years? In 10000 years? In 10 years?

Bottom line, is there a way to calculate this? Is there a way to measure or calculate IEEE 754 binary64 accuracy in specific range of numbers? What is the accuracy of IEEE 754 binary64 around 1710402835?


Solution

  • Python does not specify the resolution of the time returned by time.time(). Some systems only return a whole number of seconds, which has no error until 253+1 seconds beyond the epoch, over 285 million years from now. But of course the time will be off since only a number of seconds will have been returned, giving you no information about when in the current second the time is. So numerical accuracy is irrelevant for such systems.

    Next, we can consider systems that provide finer resolution. The significand of the IEEE-754 binary64 is 53 bits, so the spacing between representable times around 1,710,402,835 seconds (between 230 and 231 seconds) is 2−22 (230−52) seconds. So, if a system accurately knows the time and reports it, the numerical error induced by representation in binary64 is at most 2−22 seconds. (Half that if it rounds instead of truncates. Rounding is usually a default, but truncation might be something you want to do when working with times.)

    Are you doing something that needs time more accurately than a millionth of a second? Is your system clock set that well?

    In other words, for how long will the calculation accuracy of time.time() + timeout_s be within 1 millisecond?

    Consider some start time t and some timeout duration d. We wish to calculate t+d. Unfortunately, all we have is:

    • t', the time of the system clock when interrogated.
    • t, the result of converting t' to binary64.
    • d, the result of converting d to binary64 (making timeout_s).
    • t+d, the result of adding t and d and rounding the result to binary64.

    t' will differ from t due to inaccuracy of clock setting, inaccuracy of the clock in tracking time, and the time it takes to interrogate it. We do not know these errors from information in the posted question. For this answer, I will treat them as zero and disregard them.

    The error between t' and t may be zero if the clock is maintained in a way that matches the binary64 format, such as ticking 2n times per second for some n. However, given some arbitrary measurement t', the conversion should be at most one unit of least precision (ULP) of the format. As stated above, for t' around 1,710,402,835 seconds, that is 2−22 seconds, and the rounding error will be half that if round-to-nearest is used.

    d is presumably relatively small, less than a second. If it is less than one second, its ULP is less than 2−52, so the error in converting it to binary64 to produce timeout_s is less than 2−53 if round-to-nearest is used.

    When t and timeout_s are added, the operation introduces another error of at most 2−23, using round-to-nearest, for t + timeout_s around 1,710,402,835 seconds. So our total error is less than 2−22 + 2−53 + 2>−23.

    This bound grows as t + timeout_s. t + timeout_s can grow in two ways: t increases or timeout_s increases. From the problem statement, it appears timeout_s is intrinsic to the application and will not grow over time. So we are concerned about t growing.

    In this case, the bound crosses the millisecond range when the ULP of t reaches 1/1024, at which point the bound is 1/1024 + 2−53 + 1/2048. The ULP is 1/1024 = 2−10 when t reaches 2−10 + 52 = 242 = 4,398,046,511,104, over 139,000 years from now.

    Is there a way to measure or calculate IEEE 754 binary64 accuracy in specific range of numbers?

    We can answer about the precision of the format but not the accuracy of any numbers. Accuracy is a function of the quality of the provided data (such as how well the clock is set) and errors introduced in computations performed with it. For example, 3 can be represented exactly and is a perfectly accurate number of sides in a triangle even though the resolution of the binary64 format around 3 is 2−51, but it is a grossly inaccurate number of stars in the universe.

    The spacing between a representable normal number x and the next greater magnitude representable number is 2floor(log2 x) − 52, within the finite range of the format.