今天遇到一个有趣的问题,就是浮点数什么时候是无效的,或者说is Not a Number (Nan),坦白说,写了这么久(有10年了吧)也没有留意过这个问题,开始遇到这个问题的时候,最先想到的还是google,baidu,顺便用了下我们公司的有道(www.yodao.com,做个广告),都没有找到完美的答案,相反寻求答案的帖子到随处都是,看来还是有很多人碰到过这个问题,经过一番测试,给出一个比较完备的list:
形如 | 结果 | Vc表示为 |
0/0 | NaN | -1.#IND |
N/0 (N>0) | Infinite | 1.#INF |
-N/0 (N>0) | -Infinite | -1.#INF |
0 * INF | NaN |
|
0 * -INF | NaN |
|
0 / INF | 0 |
|
INF * INF | Infinite |
|
INF * -INF | -Infinite |
|
INF / 0 | Infinate |
|
N * INF | INF |
|
NaN 参与计算 | NaN |
|
以上结果是在vc2005中浮点数计算的测试结果,默认情况下,在vc2005中除0.0计算不会抛出异常,只是计算的结果可能是NaN或者Inf,这在实际的编程中是件让人头疼的事情,我们会不知不觉陷入浮点异常的陷阱,最终的结果不如预期,而要找到这个浮点计算的罪魁祸首可能需要很长时间的调试,因为NaN和Inf还是可以继续参与计算的。
为了避免浮点计算异常,我们可以通过_controlfp函数设置开启浮点异常检查:
_controlfp(_EM_INVALID,_MCW_EM);
这样当计算出现INF,NaN的时候,会抛出异常,这样变可以快捕捉错误,从而处理,更多信息可以参考MSDN,/fp (Specify Floating-Point Behavior)章节。
知道了浮点出会出现NaN,如果不抛出异常的情况下,我们如何判断一个浮点数时不时NaN呢?使用_isnan函数吧,这里需要注意的是INF是一个合法的浮点数,及is a number,所以_isnan函数的检查是可以通过,如果想知道一个float是不是infinite,使用_finiate函数可以检查这样的情况?
转自:http://www.sineysoft.com/blog/post/float_exception.html
浮点异常值:NAN,QNAN,SNAN
32位浮点数在机器中的表示按照IEEE的标准是这样的:
+------+----------------+-------------------------------+
| 1bit | 8bit | 23bit |
+------+----------------+-------------------------------+
其中:1bit表示符号位(0表示正,1表示负),8bit表示指数(0~255,实际指数取值还要减去127,即指数取值区间为-127~128),23bit表示尾数。
这里所要说的浮点异常值就是这种表示产生的几种特殊值,IEEE规定根据指数和尾数的不同分别可表示如下几种特殊值:
1. 零值:按上述的浮点表述形式如果指数部分全部为0,并且尾数全部为0,则表示为浮点0.0,并且规定-0 = +0
2. 非规格化值:如果指数全部为0,尾数非0,则表示非规格化的值,16进制看到的就是[80xxxxxx]h或者[00xxxxxx]h
3. 无穷值:如果指数全部为1,尾数全部为0,则根据符号位分别表示正无穷大和负无穷大,16进制看到的就是[FF800000]h或者[7F800000]h
4. NAN:主角来了,如果指数全部为1,尾数非0,则表示这个值不是一个真正的值(Not A Number)。NAN又分成两类:QNAN(Quiet NAN)和SNAN(Singaling NAN)。QNAN与SNAN的不同之处在于,QNAN的尾数部分最高位定义为1,SNAN最高位定义为0;QNAN一般表示未定义的算术运算结果,最常见的莫过于除0运算;SNAN一般被用于标记未初始化的值,以此来捕获异常。
那么既然NAN不是一个真实的数值,在程序如何判断变量是否变成了NAN呢?大部分语言中针对NAN值都有一系列的函数定义,C语言中最常见的三个函数:
_isnan(double x); //判断是否为NAN
_finite(double x); //判读是否为无穷大
_fpclass(double x); //返回一系列的定义值,如:_FPCLASS_QNAN, _FPCLASS_SNAN,具体参考MSDN