β

Double.NaN != Double.NaN

kafka0102的边城客栈 209 阅读

昨天做数据时发现个很诡异的情况,当然,再诡异的技术现象也有发生原因。抛开我的问题来说,我可以再提出个问题:是否在某种编程语言中,存在着那么一个变量i,使得i!=i成立,也使得while(i==i);可以成功的退出?如果你想到并理解了,你可以终止阅读这篇可能冗长的文章,尽管为了填补这个月的博客空白我刻意小题大作一番。在java语言中,Double.NaN就能使得上面两个表达式成立。

说起浮点型中的NaN,之前的少有印象就是来自javascript,而考虑到没怎么搞过js,这点印象也就仅限于曾经alert出来的令人惊异的NaN(以及undefined)了。先说说浮点型吧,它的标准出自IEEE 754,Java规范中的章节是http://java.sun.com/docs/books/jls/third_edition/html/typesValues.html#4.2.3,两篇也挺冗长的英文内容。可以重点提及的是,在内部实现上,32位的float和64位的double是使用int和long表示的。具体的表示方法可以从java api文档上看到更好的解释。以Double为例,它提供了下面3个方法:

分析发现,除了常规的double取值,NaN(not a number)并不是对应着唯一的表示,尽管它并不是一个数值,比如除0或者对负数取平方根的结果都是NaN,但它们的发生原因是不一样的,所以如果是signaling NaN,可以通过比对位来得到更多的信息,尽管这在多数语言(包括Java)中是不可见的。那么,我们来看看Double.NaN会带来什么特别之处吧:

1)Double.NaN != Double.NaN。既非数值,何来相等。和Double.NaN相关的比较操作结果都是false,所以,Double.NaN > 1.0为false,Double.NaN < 1.0为false,Double.NaN == 1.0也为false。不过,Double a = new Double(Double.NaN);a.equals(a)==true;是成立的,因为对象a比较的是引用而不是数值。

2)Double.NaN + Double.NaN -> Double.NaN
3)Double.NaN + 1.0 -> Double.NaN
4)(int) (Double.NaN) -> 0。我的问题也是因为这个转换,以致我不可思议的盯着那些本不应该为0的结果。

从上可以看出,在Double.NaN存在时,既有的逻辑判断和数值运算可能走向未料想的方向。从语言设计角度来说,Java接纳了浮点型规范中的NaN,不像整形那样抛出异常,但这却带来潜伏的bug陷阱。看看其它语言的做法吧,C/C++会毫不留情的出core,ruby、python会报错,即便是一塌糊涂的PHP,至少还会报个warning,是不是Java的表弟C#有可能接纳了NaN?要我说啊,还是抛个异常好些,既非数值,要你添个毛乱阿。

作者:kafka0102的边城客栈
要有最朴素的生活与最遥远的梦想,即使明日天寒地冻、路远马亡。
原文地址:Double.NaN != Double.NaN, 感谢原作者分享。

发表评论