函数 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,公式偏高
x的correct 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)
)。
在浮点运算中,浮点数的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,公式偏高
x的correct 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)
)。