std::isnan()和std::isinf()是C++11起用于检测浮点数是否为NaN或无穷大的标准函数,定义在<cmath>中,专用于float/double/long double类型,不可用==比较NaN因其不等于自身。

遇到 NaN 或 Inf 就用 std::isnan() 和 std::isinf() 判断
浮点计算中常出现无效值,比如除零、开负数、溢出后变成 NaN 或 Inf。直接参与后续计算会污染结果,甚至让整个数组变全 NaN。C++11 起标准库提供了 std::isnan() 和 std::isinf(),必须搭配 <cmath> 使用,且只对浮点类型有效(float/double/long double)。
常见错误是拿 == 比较 NaN:val == std::nan("") 永远为 false —— 因为 NaN != NaN 是 IEEE 754 规定。所以不能写 if (val == NaN),必须用 std::isnan(val)。
示例:
#include <cmath>
#include <vector>
<p>std::vector<double> data = {1.5, 2.0, std::nan(""), 3.7, std::numeric_limits<double>::infinity()};
for (double x : data) {
if (std::isnan(x) || std::isinf(x)) continue;
// 正常处理 x
}</p>读文件时跳过解析失败的行,用 stream.fail() + stream.clear() + stream.ignore()
从文本文件读数值时,遇到字母、空行、格式错乱,operator>> 会失败并置位 failbit,后续所有读取都静默失败,循环就卡死或重复读同一行。
立即学习“C++免费学习笔记(深入)”;
关键不是“跳过”,而是“恢复流状态”:先用 stream.fail() 检测,再调 stream.clear() 清标志位,最后用 stream.ignore(...) 跳过当前坏行(否则下次还读它)。
要点:
-
ignore(std::numeric_limits<std::streamsize>::max(), '\n')才能真正跳到下一行 - 别漏掉
clear(),否则ignore()不执行 - 如果文件混合整数/浮点,优先读
std::string再用std::stod等转换,可单独捕获std::invalid_argument
容器遍历时删元素?别在 for (auto x : container) 里用 continue
范围 for 循环(range-based for)本质是按迭代器隐式遍历,continue 只跳过本次循环体,不影响迭代器推进。但如果你本意是“跳过某些元素不处理”,这没问题;可一旦想“跳过并删除”,这就错了 —— 删除操作会让迭代器失效,而范围 for 无法感知。
正确做法分两种场景:
- 只跳过不删:用
if (is_invalid(x)) continue;完全安全 - 要删再跳过:改用传统 for + 迭代器,配合
erase()返回的新迭代器,例如:it = vec.erase(it);(此时不要++it) - 批量删:用
std::remove_if()+erase()惯用法,避免手动管理迭代器
std::optional<T> 是更现代的“无效数据”建模方式
如果你的数据源本身支持“有/无值”语义(比如函数可能查不到配置项、API 返回可能缺失字段),比用魔法值(-1、0、NaN)更安全的是 std::optional<T>。它把“无效”显式编码进类型系统,编译期就防错。
循环中判断:if (!opt_val.has_value()) continue; 或直接解包 if (auto val = opt_val) { /* use *val */ }。
注意点:
-
std::optional不适用于原始 C 风格数组或纯 POD 结构体字段(需额外空间和构造) - 和
std::variant不同,它不表示多种有效类型,只表示“T 是否存在” - 传参时避免值传递
std::optional,尤其大对象,优先 const 引用
无效数据的边界往往不在语法,而在语义 —— 同一个 0.0,可能是有效测量值,也可能是初始化未赋值的占位符。选判断方式前,先确认“无效”的定义是从哪来的:是计算异常?输入格式错误?还是业务逻辑上明确缺失?


















