我在 Microsoft Visual Studio Community 2019 中用 C++ 编写了一个代码片段,如下所示:
int m = 11;
int p = 3;
float step = 1.0 / (m - 2 * p);
可变步长是0.200003,我想要的是0.2。有什么提高精度的建议吗?
这个问题来自UNIFORM KNOT VECTOR。节点 vector 是 NURBS 中的一个概念。你可以认为它只是一个像这样的数字数组:U[] = {0, 0.2, 0.4, 0.6, 0.8, 1.0};两个相邻数字之间的跨度是一个常数。节点 vector 的大小可以根据条件改变,但范围在[0, 1]。
整个函数是:
typedef float NURBS_FLOAT;
void CreateKnotVector(int m, int p, bool clamped, NURBS_FLOAT* U)
{
if (clamped)
{
for (int i = 0; i <= p; i++)
{
U[i] = 0;
}
NURBS_FLOAT step = 1.0 / (m - 2 * p);
for (int i = p+1; i < m-p; i++)
{
U[i] = U[i - 1] + step;
}
for (int i = m-p; i <= m; i++)
{
U[i] = 1;
}
}
else
{
U[0] = 0;
NURBS_FLOAT step = 1.0 / m;
for (int i = 1; i <= m; i++)
{
U[i] = U[i - 1] + step;
}
}
}
最佳答案
让我们看看您的代码中发生了什么:
表达式
1.0/(m - 2 * p)
产生 0.2,最接近的可表示double
值为 0.200000000000000011102230246251565404236316680908203125。请注意它有多精确 – 精确到 16 位有效的小数位。这是因为,由于1.0
是一个double
字面量,分母被提升为double
,整个计算都是 double 的,因此产生一个double
值。将上一步得到的值写入
step
,float
类型。因此,该值必须四舍五入到最接近的可表示值,恰好是 0.20000000298023223876953125。
所以你引用的结果 0.200003 不是你应该得到的。相反,它应该更接近 0.200000003。
Is there any suggestion to improve the precision?
是的。将值存储在精度更高的变量中。例如,不要使用 float step
,而是使用 double step
。在这种情况下,您计算出的值不会再次四舍五入,因此精度会更高。
你能得到准确的 0.2 值用于后续计算吗?不幸的是,对于二进制浮点运算,不行。在二进制中,数字 0.2 是周期分数:
0.210 = 0.0̅0̅1̅1̅2 = 0.001100110011...2
参见 Is floating point math broken?问题及其答案以获取更多详细信息。
如果您真的需要小数计算,您应该使用库解决方案,例如Boost's cpp_dec_float
.或者,如果您需要任意精度的计算,您可以使用例如cpp_bin_float
来自同一个图书馆。请注意,这两种变体都比使用内置 C++ 二进制浮点类型慢几个数量级。
关于c++ - 如何提高计算 float 的精度?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58501421/