函数 math.ulp 是否比 Python 中的显式公式更精确?

Is the function math.ulp more precise than the explicit formula in Python?

在浮点运算中,浮点数的unit in the last place(ULP)是该数与连续数之间的间距,即其最低有效位(最右位)的值,如果它是 1。它由以下公式给出:

ULP(x) = b−(p−1) • |x|

其中 b 是基数(二进制数字为 2),p(双精度有效数字为 53)是精度。

Python 3.9 引入了一个新函数 math.ulp 来计算浮点数的 ULP。

使用此函数,对于 ULP 1:

,前面的公式按预期得到验证
>>> math.ulp(1)
2.220446049250313e-16
>>> 2**(-(53 - 1)) * abs(1)
2.220446049250313e-16

但未验证为 10−10 的 ULP 例如:

>>> math.ulp(1e-10)
1.2924697071141057e-26
>>> 2**(-(53 - 1)) * abs(1e-10)
2.2204460492503132e-26

math.ulp(x)2**(-(53 - 1)) * abs(x) 更精确吗?为什么?

CPython 实现在 Modules/mathmodule.c#L3408-L3427 但我找不到被调用函数的实现 nextafter 无法理解:

static double
math_ulp_impl(PyObject *module, double x)
/*[clinic end generated code: output=f5207867a9384dd4 input=31f9bfbbe373fcaa]*/
{
    if (Py_IS_NAN(x)) {
        return x;
    }
    x = fabs(x);
    if (Py_IS_INFINITY(x)) {
        return x;
    }
    double inf = m_inf();
    double x2 = nextafter(x, inf);
    if (Py_IS_INFINITY(x2)) {
        /* special case: x is the largest positive representable float */
        x2 = nextafter(x, -inf);
        return x - x2;
    }
    return x2 - x;
}

2−(53−1) • |x| (or 2**(-(53 - 1)) * abs(x)) 不是 ULP(x) (or math.ulp(x)) 的公式,因为它没有在x 的最低位,而是 x (1.something) 的有效数字的值缩放到 x 的最低位的位置=18=]x。当x不是2的幂时,其尾数超过1,公式偏高

xcorrect formula is 2−(53−1) • 2max(e, −1022) where e is the IEEE 754 normalized exponent,即2e≤|x| < 2e+1(或2**(-(53 - 1)) * 2**max(math.floor(math.log2(x)), -1022))。