用 std::sqrt 计算向量 L2 范数:二维写为 std::sqrt(v.xv.x + v.yv.y),三维类似,通用模板用 std::sqrt(std::inner_product(v.begin(), v.end(), v.begin(), 0.0));避免 std::pow(x,2),归一化前须检查 len != 0。

怎么用 std::sqrt 算向量长度(L2 范数)
向量长度就是各分量平方和再开方,C++ 里直接调用 std::sqrt 即可,别手写牛顿迭代或查表——标准库已足够快且跨平台。注意传入非负值,否则 std::sqrt 返回 NaN。
- 二维向量:
double len = std::sqrt(v.x * v.x + v.y * v.y); - 三维向量:
double len = std::sqrt(v.x*v.x + v.y*v.y + v.z*v.z); - 通用模板(支持
std::vector<double>):double len = std::sqrt(std::inner_product(v.begin(), v.end(), v.begin(), 0.0));
别用 std::pow(x, 2) 替代 x * x:前者函数调用开销大,还可能引入额外浮点误差。
归一化前必须检查零向量,否则除零崩溃
归一化本质是每个分量除以长度,若长度为 0(即零向量),结果未定义,/ 0.0 在 IEEE 754 下产生 inf 或 NaN,后续计算极易静默出错。
- 安全写法:先算长度
len,再用if (len 判断(不用 <code>== 0.0,浮点不可靠) - 若为零向量,通常应提前返回、抛异常或设默认方向(如
(1,0,0)),取决于业务逻辑 - 示例:
double len = std::sqrt(v.x*v.x + v.y*v.y); if (len < 1e-12) return {0, 0}; // 或 throw std::invalid_argument("zero vector"); return {v.x / len, v.y / len};
用 float 还是 double?看精度和性能权衡
游戏引擎常用 float(省内存、GPU 友好),科学计算倾向 double(避免累积误差)。但归一化本身不改变精度等级——输入 float 就用 std::sqrtf,输入 double 就用 std::sqrt。
立即学习“C++免费学习笔记(深入)”;
- 混用会隐式转换:用
float向量却调std::sqrt,触发double版本,多一次转换 - 推荐统一:定义类型别名,如
using vec3 = std::array<float, 3>;,配套写vec3 normalize(const vec3& v) -
std::sqrtf在 x86 上通常比std::sqrt快约 10–15%,但现代编译器常自动优化,不必过早纠结
Eigen 或 GLM 库的 normalized() 和 norm() 怎么用
自己写没问题,但已有成熟库时别重复造轮子。Eigen 和 GLM 都重载了 .norm() 和 .normalized(),语义清晰且做了边界处理。
- Eigen:
v.norm()返回 L2 范数;v.normalized()返回归一化副本(原向量不变);v.normalize()原地归一化 - GLM:
glm::length(v)等价于 norm;glm::normalize(v)返回新向量(不修改原值) - 注意:GLM 默认使用
float,需显式写glm::dvec3才用double
这些库内部已处理零向量(通常返回 (0,0,0) 或抛异常),但文档未必明说,建议仍查源码或单元测试确认行为。
归一化最易被忽略的是零向量检查时机——不是只在入口判断,而是每次调用 normalized() 前,都要确认上游没把未初始化或计算溢出的向量传进来。


















