(此处为人肉维护的镜像,原文请见: http://csbabel.wordpress.com/ 翻墙请用 Google Reader 查看: http://csbabel.wordpress.com/feed/ )
谁是本文的读者
本文名为提高班,所以它当然是写给初学者的。高手以及自认为高手的请勿视。如果不确定自己是否需要观看,这里提供一个小测试:但凡不会回答下面的题目的,建议你阅读本文及末尾提供的延伸阅读材料。
先来看这一段程序,如果x是C++内建的数值类型,在什么时候DayOfReckoning()函数会被调用呢?
if ( x != x ) { // WTF ???!!!
DayOfReckoning(x);
}
这个题是考计算机数值表示的。答案是:当x为浮点数的NaN的时候。
NaN,它不是一个数
NaN是Not a Number的缩写,就是说它不是一个数。NaN是定义在IEEE 754标准中的特殊值,类似的特殊值还有INF(无穷大,INFinite)。IEEE 754是定义浮点数的标准,有1985和2008两个版本。其中,2008版本还定义了十进制浮点数的标准。
NaN的产生(可以)是这样的(此段文字摘译自WIKI):
1)NaN参与数学运算的结果仍是NaN
2)0/0,正无穷大/正无穷大,正无穷大/负无穷大,负无穷大/正无穷大,负无穷大/负无穷大
3)0和正/负无穷大相乘
4)正无穷大+负无穷大,负无穷大加正无穷大
5)数学上无解的一些函数值,比如:负数的平方根,对2.0的求反正弦,等等
第一条说明了NaN在运算中的“传染性”。如果不慎在某处引入了NaN,那么之后的算式值很可能就一直是NaN了。所以在涉及浮点数值运算时一定要注意NaN的存在。
回过头来解释前面的题目:NaN永远不等于自己,所以说NaN == NaN永远为false,而NaN != NaN永远为true。这是一个相当特殊的情况:即使参与比较的两个NaN的内存表示是一模一样的,但它们仍然是不等的。
更深一步地说,NaN有两种,一种是Quiet NaN,另一种是Signalling NaN。在使用时,Signalling NaN会立即引发异常,然后将自身变为Quiet NaN;Quiet NaN的行为比较安静,在算术运算中它不会引发异常,只是将运算结果“传染”为NaN(但是在某些不接受NaN的地方仍会引发异常)。
INF,它溢出到无穷大
INF就是无穷大,在浮点数中,有+INF和-INF两种。INF在数学上有对应的概念,所以它比NaN更好理解一些。比如用1/0,得到的结果就是INF
0,它竟然还有正负
在IEEE浮点数中,0也是特殊的,因为它有+0和-0两种(“正零”和“负零”)。其中,1除以“负0”等于“负无穷大”,1除以“正0”等于“正无穷大”。
舍入,它可不是“四舍五入”
舍入算法对我们多数人来说意味着“四舍五入”。这个在上学时就学过了,但它却会给舍入之后的数带来扩大的趋势。在买卖方之间,民俗传统中一直有“五 刨六撩”一说,“五刨”是说将5及5以下的舍去,“六撩”是说将6及6以上的进位,这种舍入算法有整体减小的趋势,体现了小生意人让利的“诚意”。那么, 更好的舍入算法是什么样的呢?
IEEE754中定义了最基本的舍入算法:Rounds to nearest, ties to even(even在这里是“偶数”的意思)。这种算法将4及4以下的数抛弃,6及6以上的数进位,如果要舍入的最高位是5的话,则将要保留的最低位圆整 到最接近的偶数(这里说的“偶数”包括0)。比如:
89.64 --> 90
98.46 --> 98
1919.54 --> 1920
1918.55 --> 1918
对于大量均匀分布的数来说,这种50%概率算法保证了舍入后的数没有放大或者缩小的趋势。在银行里都是用的这种算法,做过金融类程序的对此应该印象深刻的。
除了上面提到的“Ties To Even算法”之外,还有几种舍入算法,因为定义得非常清晰和明确,这里不再多说,详情请见参考资料。
总结
即便是使用高级语言,只要使用浮点数,就有可能碰到上面提到的问题。可惜国内的教材多数只讲了浮点数最基本的存储形式,对于上面几个很重要的话题竟 然只字未提。本文存在的意义也在于此。据说,每在文章中加入一个二进制表示公式,文章的读者就会减少一半,所以我没有放任何的公式。感兴趣的可以用鼠标猛 烈敲击参考资料/延伸阅读。
参考资料/延伸阅读
1、http://en.wikipedia.org/wiki/IEEE_754-2008
2、http://en.wikipedia.org/wiki/IEEE_754-1985
3、http://en.wikipedia.org/wiki/Signed_zero
4、http://en.wikipedia.org/wiki/Not_a_Number
5、IEEE标准
致谢
感谢Jun Yang指出文中的问题(http://blog.csdn.net/lovetheme)