C++二维std::vector存储uint8_t网格赋值后部分位置取值异常问题
问题根因
- 浮点数精度丢失是核心问题。0.05属于十进制有限小数,但对应二进制是无限循环小数,无法被浮点类型精确存储。你在嵌套循环中每次累加
grid_resolution(0.05)的操作,累计多次后会产生明显的精度误差,要么实际值略小于理论值,要么略大于理论值,直接导致两类异常:- 循环终止条件判断错误,部分本应执行的循环分支被跳过,对应预期网格位置从来没有被赋值为255
- 计算
m_co、n_co时,带误差的浮点值除以0.05后得到的结果略小于理论整数,强制转int时会被截断为更小的索引值,导致赋值到了错误的网格位置,预期位置没有被修改
- 你在循环内部打印的
m_co、n_co本身就是精度误差带来的错误索引,你给错误的索引位置赋值为255,打印自然显示255;但循环结束后你访问的是预期的正确索引位置,这些位置从未被赋值,所以值为0。 - 坐标转网格索引的逻辑不严谨,直接强制转换int没有做四舍五入,同时没有做边界校验,极端情况下可能出现数组越界访问的未定义行为。
rows、cols使用浮点类型定义,传入vector构造函数时隐式转int,如果因精度问题导致10/0.05计算结果为199.9999999而非200,会直接导致网格实际大小小于预期,后续访问高索引位置属于越界操作,值是未定义的。
修复方案
完全避免浮点循环累加操作,改由整数计算网格索引范围后再遍历,从根源上消除精度问题:
- 先将所有浮点坐标转换为整数网格索引,转换时做四舍五入处理
- 直接计算需要赋值的索引上下界,整数遍历赋值
- 增加边界校验,避免越界访问
修复后代码如下:
#include <iostream> #include <vector> #include <cmath> using namespace std; int main() { const float grid_width = 10.0f; const float grid_height = 10.0f; const float grid_resolution = 0.05f; // 计算行列数时先四舍五入再转int,避免精度导致大小错误 const int rows = static_cast<int>(round(grid_width / grid_resolution)); const int cols = static_cast<int>(round(grid_height / grid_resolution)); vector<vector<float>> landmarks = {{2,2},{8,8},{2,8},{8,2},{3,2},{3.5,2.1},{4.0,3.5},{4.5,2.0},{5.0,3.0}}; vector<vector<uint8_t>> grid(rows, vector<uint8_t>(cols, 0)); // 周边3x3对应偏移量为1,如果需要0.1米范围(2个网格单位)可改为2 const int OFFSET = 1; for(const auto& pt : landmarks) { // 坐标转索引时四舍五入,避免截断误差 int x_co = static_cast<int>(round(pt[0] / grid_resolution)); int y_co = static_cast<int>(round(pt[1] / grid_resolution)); // 计算索引范围,同时做边界裁剪避免越界 int x_start = max(0, x_co - OFFSET); int x_end = min(rows - 1, x_co + OFFSET); int y_start = max(0, y_co - OFFSET); int y_end = min(cols - 1, y_co + OFFSET); // 整数遍历赋值,完全消除浮点精度问题 for(int x = x_start; x <= x_end; x++) { for(int y = y_start; y <= y_end; y++) { grid[x][y] = 255; } } } // 测试输出 cout << unsigned(grid[18][18]) << endl; cout << unsigned(grid[18][22]) << endl; cout << unsigned(grid[22][22]) << endl; cout << unsigned(grid[22][18]) << endl; cout << unsigned(grid[17][17]) << endl; cout << unsigned(grid[20][18]) << endl; return 0; }
内容的提问来源于stack exchange,提问作者QuickSilver
相关产品推荐
相关产品推荐

