本文经授权转自公众号CSDN(ID:CSDNnews)
, T$ G/ C! G& g& Y作者 | Alex Syniakov- P+ T8 v' i+ ~7 a4 a
翻译 | 郑丽媛本文作者希望通过这篇文章,让开发者深入了解 C++ 中的 NaN 值及其有效处理方式。; h( R8 F( L/ x" b! i+ m0 e7 w
原文链接:https://alexsyniakov.com/2024/03/20/understanding-nan-numbers-in-c-and-their-properties/
7 ]8 M- `; W( D/ aNaN,即“Not a Number”,表示在实数范围内无法表达的数值。它的特殊性质,使得在编程(尤其是在 C++ 中)中处理 NaN 值成为许多开发人员感兴趣的话题。为此,本文将深入探讨什么是 NaN 值、它们是如何产生的、它们的特性以及如何在 C++ 中有效地处理它们。# Z7 G/ D9 G, R% N& F- R! z+ y! O
0 X0 B W; i' o# i' F. |& Z+ X0 ?9 l4 Z7 O6 X0 b
一、NaN 是如何产生的?9 o* {. ] D: u" {0 O2 @3 I
NaN 值可能由不产生确定或实数结果的操作而产生,常见例子包括:0 r2 ~2 j; V+ [2 O& B) b& \
零除以零;
- @: s( R) Z# T9 x; b, F无穷大除以无穷大;
8 C$ x( N1 j3 | v8 p0 L$ ~零乘以无穷大;
$ k% g% `) N G+ \带相反符号的无穷级数相加;
) I! e$ N, M! M8 F+ f( a计算负数的平方根;
9 f& u7 K. g, L; I6 l5 T7 d B取负数的对数;
0 {* L0 H3 _5 m* H使用非数字操作数进行复杂的数学运算。
; m3 X9 o) T/ Z! B2 \% e7 l/ G此外,在 C++ 中,还可以使用 std::nan(const char*) 或 std::numeric_limits::quiet_NaN() 来明确地创建 NaN 值。. Q( u" w8 c: t+ L; A7 x# i0 s# B1 \
1 v! ~7 r: A s T' z, ~
/ L4 U) A; k0 H, L$ k! v6 y- M8 f, D1 ^
二、NaN 的特性
0 V) k, Q$ t( k* V- s ~NaN 的一个显著特点是在比较时的行为。在全等运算中,NaN 与其他任何值相比,结果都是 false。示例如下:
5 s) L5 q R9 p( G0 _double nanValue = 0.0 / 0.0;bool alwaysFalse = nanValue == nanValue; // falsebool alwaysTrue = nanValue != nanValue; // true要确定一个值是否为 NaN,可将该值与自身进行比较:
* k0 Y) Q+ N- T3 obool isNan = nanValue != nanValue;此外,在 C++ 中,有一个内置函数 std::is_nan(从 C++11 开始)也可以让你检查一个数字是否为 NaN。
0 X2 `# O+ O0 E3 d- l
6 V" D$ F9 b: Q/ E' v0 |
0 \" H* D! Q. ]5 V" r4 d三、NaN 对数据结构的影响
+ }: y' Z$ ~% y1 z因此,如果您在计算中得到了 NaN 作为结果,然后将此值用于关联容器中,例如:* ?/ f2 C7 y6 `* w
std::setdouble> test;test.insert(NaNvalue);test.insert(1.0); // 不会插入 test.insert(2.0); // 不会插入此后,由于 NaN 不满足关联容器键的严格弱排序要求,无法向集合中插入任何值。
" e) ~' \: X: @6 E' B2 n( d$ e" V
! E1 s3 ^+ e+ ]四、NaN 的标准和类型, l- J1 W7 W. _2 B' M- K- {
IEEE 754 标准将 NaN 值分为“quiet NaNs”(qNaNs)和“signaling NaNs”(sNaNs)。Quiet NaNs 允许计算继续进行而不受干扰,在算术运算中悄无声息地传播。而 Signaling NaNs 则会触发异常,立即处理无效操作。尽管这两种 NaN 类型的区别很重要,但在 C++ 或 IEEE 754 标准库接口中并没有明确处理,更多地取决于底层硬件和编译器行为。/ Y' U; J4 H% f% |" V3 _; j s
1 r7 [4 i4 A; Y# Q: o
五、检查 NaN
5 z* W7 s9 J0 K, d6 B" y各种数学库都提供了 NaN 检查函数,例如,glm 有一个函数,用于检查向量的各个分量是否为 NaN,并返回一个布尔向量:
% S6 |. }1 X) s; F' I( zglm::dvec3 NaNvector = someFunction();bool isNaN = glm::all(glm::isnan(NaNvector));一个更好的做法是,不仅检查一个数字是否为 NaN,还应该进行更全面的检查——判断数字是否有限。为此,std 提供了函数 std::isfinite(从 C++11 开始),在 glm 中也有类似的向量函数。, V- t% g0 F3 ]/ ]9 i& m
std::println("{}", std::isfinite(std::numeric_limitsdouble>::quiet_NaN()));
6 y# S' F- o6 Mstd::println("{}", std::isfinite(std::numeric_limitsdouble>::infinity()));9 t( G% r1 z6 F" R! I9 o
std::println("{}", std::isfinite(-std::numeric_limitsdouble>::infinity()));; a1 K( L' X( r
std::println("{}", std::isfinite(0.0));9 g* B8 n5 @ d
std::println("{}", std::isfinite(std::exp(1000)));
' E9 \3 j3 O8 Qstd::println("{}", std::isfinite(std::numeric_limitsdouble>::min()));
5 S* p& y( ^" O! O( p. i3 y六、结论
0 H' u9 p3 `* m) y在 C++ 中处理 NaN 值需要了解它们的生成、属性以及对数据结构的影响。通过利用内置函数并遵循最佳实践,开发人员可以有效管理 NaN 值,确保他们的软件可以优雅地处理边缘情况,并在整个计算过程中保持数值的完整性。
3 k! W/ q; P) @5 o) l7 F# l本文转自公众号“CSDN”,ID:CSDNnews——EOF——你好,我是飞宇,本硕均于某中流985 CS就读,先后于百度搜索、字节跳动电商以及携程等部门担任Linux C/C++后端研发工程师。
' _/ |3 a) T6 s1 X最近跟朋友一起开发了一个新的网站:编程资源网,已经收录了不少资源(附赠下载地址),如果屏幕前的靓仔/女想要学习编程找不到合适资源的话,不妨来我们的网站看看,欢迎扫码下方二维码白嫖~
; S) |6 l! V" e7 y4 z/ a0 H9 w% } [/ D$ W
llork2rdcnd64073945741.gif
, @* y2 ]) Q$ m$ Z A9 G, t5 K+ Z
& _# B M7 R* L! n% b2 O同时,我也是知乎博主@韩飞宇,日常分享C/C++、计算机学习经验、工作体会,欢迎点击此处查看我以前的学习笔记&经验&分享的资源。. V( B0 y1 h# u z
我组建了一些社群一起交流,群里有大牛也有小白,如果你有意可以一起进群交流。
3 ]) V1 `9 F. \* w' \! B
skmb2anu4d464073945842.png
/ \# V& d! h5 y# M" e( G& h欢迎你添加我的微信,我拉你进技术交流群。此外,我也会经常在微信上分享一些计算机学习经验以及工作体验,还有一些内推机会。
/ k6 [. D: K- g+ Y' w, p, t! V
" a6 c$ F6 B2 o. ]: E
m1tlfbpflgp64073945942.png
8 j' K' j& n- ~9 I, \" {! F% M2 l
加个微信,打开另一扇窗/ O$ v. U! U7 G' \6 F" e( K3 A
du3bwnwxbf264073946042.gif
|