无论在哪个平台上,权重之和都应该是1.0

sum of weights should be exactly 1.0 no matter on which platform it is

本文关键字:权重 平台      更新时间:2023-10-16

我有这样一个函数,可以根据高斯分布计算权重:

const float dx = 1.0f / static_cast<float>(points - 1);
const float sigma = 1.0f / 3.0f;
const float norm = 1.0f / (sqrtf(2.0f * static_cast<float>(M_PI)) * sigma);
const float divsigma2 = 0.5f / (sigma * sigma);
m_weights[0] = 1.0f;
 for (int i = 1; i < points; i++)
 {
     float x = static_cast<float>(i)* dx;
     m_weights[i] = norm * expf(-x * x * divsigma2) * dx;
     m_weights[0] -= 2.0f * m_weights[i];
 }

在以上所有计算中,数字并不重要。唯一重要的是m_weights[0] = 1.0f;,每次我计算m_weights[i]时,我都会像这样从m_weights[0]中减去两次:

m_weights[0] -= 2.0f * m_weights[i];

以确保CCD_ 4将与CCD_。但事实并非如此。此断言失败:

float wSum = 0.0f;
for (size_t i = 0; i < m_weights.size(); ++i)
{
    float w = m_weights[i];
    if (i == 0) {
        wSum += w;
    } else {
        wSum += (w + w);
    }
}
assert(wSum == 1.0 && "Weights sum is not 1.");

如何确保所有平台上的总和为1.0f

你不能。浮点不是那样的。根据使用的cpu,即使添加相同的值也会产生不同的结果。

您所能做的就是定义一些精度值,并确保最终得到1.0+/-该值。

请参阅:http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html

因为浮点运算的精度只有23位(例如。https://en.wikipedia.org/wiki/Single-precision_floating-point_format),舍入误差会迅速累积,因此即使其余代码都是正确的,你的和也会变成1.0000001或0.9999999(顺便问一下,你有没有在调试器中看过它,或者试图将它打印到控制台?)。为了提高精度,您可以将float替换为double,但总和仍不完全是1.0:误差会更小,类似于1e-16而不是1e-7。第二件事是用范围比较来代替与1.0的严格比较,比如:

assert(fabs(wSum - 1.0) <= 1e-13 && "Weights sum is not 1.");

这里,1e-13是ε,在ε内,两个浮点数相等。如果你选择float(而不是double),你可能需要类似1e-6的epsilon。

根据权重的大小和点数的多少,累积误差可能会大于ε。在这种情况下,您需要特殊的算法来保持更高的精度,例如在从最小的数字开始求和之前,根据数字的绝对值对数字进行排序。

如何确保所有平台上的总和为1.0f?

正如其他答案(和评论)所说,由于浮点计算的不精确性,您无法实现这一点。

一种解决方案是,不使用double,而是使用定点或多精度库,如GMP、Boost Multiprecision library或其他许多库中的一个。