首页 > 解决方案 > 函数 math.ulp 是否比 Python 中的显式公式更精确?

问题描述

在浮点运算中,浮点数的最后一位(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;
}

标签: pythonfloating-pointnumerical-analysis

解决方案


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

正确的公式是 2 −(53−1) • 2 max( e , −1022)其中ex的 IEEE 754归一化指数,即 2 e ≤ | x | < 2 e +1(或)。2**(-(53 - 1)) * 2**max(math.floor(math.log2(x)), -1022)


推荐阅读