c++ - 如何提高计算浮点数的精度?
问题描述
我在 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。结向量是 NURBS 中的一个概念。你可以认为它只是一个这样的数字数组: U[] = {0, 0.2, 0.4, 0.6, 0.8, 1.0}; 两个相邻数字之间的跨度是一个常数。节点向量的大小可以根据某些条件改变,但范围在[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
值。将上一步获得的值写入
step
,其类型为float
。所以这个值必须四舍五入到最接近的可表示值,恰好是 0.20000000298023223876953125。
所以你引用的 0.200003 结果不是你应该得到的。相反,它应该更接近 0.200000003。
有什么提高精度的建议吗?
是的。将值存储在更高精度的变量中。例如,代替float step
,使用double step
。在这种情况下,您计算的值不会再次四舍五入,因此精度会更高。
你能得到精确的 0.2 值在随后的计算中使用它吗?不幸的是,对于二进制浮点算术,没有。在二进制中,数字 0.2 是一个周期分数:
0.2 10 = 0.0̅0̅1̅1̅ 2 = 0.0011 0011 0011... 2
请参阅浮点数学是否损坏?问题及其答案以获取更多详细信息。
如果你真的需要十进制计算,你应该使用库解决方案,例如Boost'scpp_dec_float
。或者,如果您需要任意精度的计算,您可以使用例如cpp_bin_float
来自同一个库的。请注意,这两种变体都将比使用内置 C++ 二进制浮点类型慢几个数量级。
推荐阅读
- javascript - 等待量角器与刷新期间捕获警报的页面同步时出错
- php - 在 phpword 中,如果您使用自动 TOC
- python - Django - 无法从另一个应用程序导入模型文件
- ios - 将 GestureRecognizer 添加到 UIView 不会触发操作
- javascript - 用 1 个文件替换文件夹中的所有文件,但在 Gulp 中保留它们的原始名称
- java - 将二叉搜索树扩展到红黑树
- python - 如何声明多个变量?
- html - 如何将内联 SVG 移动到 CSS
- excel - 如何检索 Outlook EntryID 并将 Outlook 邮件项目作为附件发送
- javascript - 这个语法是什么意思?anArray[source].src = sourceImg;