2009-05-27

一个容易被忽视的Java整数溢出错误:无法正确获得年份之差的原因

Posted in Java at 13:17 Author:仲远

标签:

随着计算机以及编程语言的不断发展,或许我们现在对于各种类型变量的上限已经不如C语言中的那么敏感。例如,int类型(整型)的变量,它的上限值已经远远超过以前C语言中的整型变量的上限。在一个典型的C语言中,int型变量的范围为-32768~32767,而在java中,int型的变量的范围就达到了-2147483648 到2147483647。因此,通常情况下,我们不用考虑溢出问题。但是,最近我在使用java计算时间差的时候,却遇到了一个诡异的错误,经过一段时间的检查,才发现居然是整型溢出的错误。

例如,我们现在打算计算两个时间点的年份之差,虽然有很多的函数能够帮我们解决这个问题(例如,先都取出年份值,再相减)。但是也可以直接通过两个时间点之间的数值差(因为,时间类型的数据在计算机中实际上是以long型的数值来存储的),来计算这种时间差。下面演示的是错误的代码

SimpleDateFormat sdf=new SimpleDateFormat(”yyyy-MM-dd HH:mm:ss”);  
Date date = sdf.parse(”2008-05-24 00:00:00″);
Date date2 = sdf.parse(”2009-05-24 00:00:00″);  
System.out.println((date2.getTime()-date.getTime())/((long)(1000*3600*24*365)));

其中,这段代码的最主要思想就是利用时间点2(date2)的数值,减去时间点1(date)的数值,然后除以1000,将毫秒数值转为秒;再除以3600*24,转为天;最后除以365转成年。代码的逻辑上是没有错误的(当然,严格上来说,应该区分365天或是366天,因而计算年份差是不推荐这么做的),但是执行的时候却总是出错,得到的结果为21!明眼人应该一下子就能够看出这两个时间点的年份差为1年吧。于是考虑将最后一行代码改为如下:

System.out.println((date2.getTime()-date.getTime())/((long)(1000*3600*24)));

发现就能够得到正确的结果:366天。于是再次将代码改为如下:

System.out.println((date2.getTime()-date.getTime())/((long)(1000*3600*24))/365);

发现就能够得到正确的结果了:1年。但是如果将代码又改为第一种情况,就发现还是计算错误。在这种情况下,最有可能的原因就是变量的溢出问题。我们可以计算一下,1年的毫秒数为:

1000*3600*24*365 = 31536000000 ms/year

原来已经远远超过了int型变量的最大值:2147483647了。于是将计算代码改为如下,也能够正确获得结果

System.out.println((date2.getTime()-date.getTime())/(((long)(3600*24*1000))*((long)(365))));

看来,虽然计算机的支持能力以及编程语言的发展,已经能够然我们很便捷的使用各种变量,但是有时候还是需要注意一下这些类型变量所支持的范围,避免以为超过范围,造成莫名其妙的溢出错误。

本文可以自由转载,转载时请保留全文并注明出处:
转载自仲子说 [ http://www.wangzhongyuan.com/ ]
原文链接:

Leave a Comment

*
To prove you're a person (not a spam script), type the security text shown in the picture. Click here to regenerate some new text.
Click to hear an audio file of the anti-spam word