This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author vstinner
Recipients larry, serhiy.storchaka, vstinner, vxgmichel
Date 2020-02-01.00:59:08
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1580518748.47.0.557874428735.issue39484@roundup.psfhosted.org>
In-reply-to
Content
>>> a / 10**9 <= b
    False

Try to use a/1e9 <= b.

--

The C code to get the system clock is the same for time.time() and time.time_ns(). It's only the conversion of the result which is different:

static PyObject *
time_time(PyObject *self, PyObject *unused)
{
    _PyTime_t t = _PyTime_GetSystemClock();
    return _PyFloat_FromPyTime(t);
}

static PyObject *
time_time_ns(PyObject *self, PyObject *unused)
{
    _PyTime_t t = _PyTime_GetSystemClock();
    return _PyTime_AsNanosecondsObject(t);
}

where _PyTime_t is int64_t: 64-bit signed integer.


Conversions:

static PyObject*
_PyFloat_FromPyTime(_PyTime_t t)
{
    double d = _PyTime_AsSecondsDouble(t);
    return PyFloat_FromDouble(d);
}


double
_PyTime_AsSecondsDouble(_PyTime_t t)
{
    /* volatile avoids optimization changing how numbers are rounded */
    volatile double d;

    if (t % SEC_TO_NS == 0) {
        _PyTime_t secs;
        /* Divide using integers to avoid rounding issues on the integer part.
           1e-9 cannot be stored exactly in IEEE 64-bit. */
        secs = t / SEC_TO_NS;
        d = (double)secs;
    }
    else {
        d = (double)t;
        d /= 1e9;
    }
    return d;
}

PyObject *
_PyTime_AsNanosecondsObject(_PyTime_t t)
{
    Py_BUILD_ASSERT(sizeof(long long) >= sizeof(_PyTime_t));
    return PyLong_FromLongLong((long long)t);
}

In short, time.time() = float(time.time_ns()) / 1e9.

--

The problem can be reproduced in Python:

>>> a=1580301619906185300
>>> b=a/1e9
>>> a / 10**9 <= b
False

I added time.time_ns() because we loose precision if you care about nanosecond resolution, with such "large number". float has a precision around 238 nanoseconds:

>>> import math; ulp=math.ulp(b)
>>> ulp
2.384185791015625e-07
>>> "%.0f +- %.0f" % (b*1e9, ulp*1e9)
'1580301619906185216 +- 238'

int/int and int/float don't give the same result:

>>> a/10**9
1580301619.9061854
>>> a/1e9
1580301619.9061852

I'm not sure which one is "correct". To understand the issue, you can use the next math.nextafter() function to get the next floating point towards -inf:

>>> a/10**9
1580301619.9061854
>>> a/1e9
1580301619.9061852
>>> math.nextafter(a/10**9, -math.inf)
1580301619.9061852
>>> math.nextafter(a/1e9, -math.inf)
1580301619.906185

Handling floating point numbers are hard.

Why don't use only use integers? :-)
History
Date User Action Args
2020-02-01 00:59:08vstinnersetrecipients: + vstinner, larry, serhiy.storchaka, vxgmichel
2020-02-01 00:59:08vstinnersetmessageid: <1580518748.47.0.557874428735.issue39484@roundup.psfhosted.org>
2020-02-01 00:59:08vstinnerlinkissue39484 messages
2020-02-01 00:59:08vstinnercreate