随笔 - 17  文章 - 49  trackbacks - 0
<2006年9月>
272829303112
3456789
10111213141516
17181920212223
24252627282930
1234567

常用链接

留言簿(1)

随笔分类(17)

随笔档案(17)

相册

最新随笔

搜索

  •  

最新评论

阅读排行榜

评论排行榜

 

2006 913 星期三

不要用floatdouble来进行精确的小数计算

 

什么?难道它们不就是为了小数计算而生的么?在我看到 effective java item31 的时候,发出了这个孤陋寡闻的疑问。

 

知其然:

为什么说不能用 float double 来进行精确小数计算呢?

试试执行这样的语句:

System.out.println( 1.03   -  . 42 ); // 答案是0.6100000000000001 !

System.out.println(
1.00   -   9 * . 10 ); // 答案是0.09999999999999995 !

你会发现结果和你的小学数学课程有冲突。

 

知其所以然

之所以出现这样的奇怪答案,是因为 float double 不能精确的表达 0.1 ,或者任何 10 的负 n 次方。他们是设计来进行科学和工程上的计算,提供精确的近似值的。它们在涉及金融方面的计算则是不在行的。因为金融方面要求绝对的精确。

 

解决方法

BigDecimal int 或者 long

 

 

BigDecimal bd  =   new  BigDecimal( " 1.00 " );

 

 

把所有牵涉到的小数都以这种方式转变为 BigDecimal 对象,然后用它提供的方法进行计算。

你就得到了精确的计算结果,随之而来有两个小缺点,一个。。。很显然,就是比直接用原始类型要麻烦,也不能用 +,-,*,/ 这些直观的符号了;第二个就是速度会慢一点,如果不是用于大量的循环之中,则不是那么要紧。不过,你也同时得到了一个好处, BigDecimal 类还带了丰富的舍入方法,也是不错的。

如果小数位本身不长,则可以用 int 或者 long 来替代,我想你明白这个方法是什么意思的。在速度必须很快的位置,你又不介意自己看着小数点位,这个还是可用的,但如果数字本身超过了 18 位,就只能用 BigDecimal 了。

 

Wednesday, September 13, 2006

Don’t use float and double when exact answers are required

 

What? Aren’t calculating decimal are what they are design for? I made such an ignorant question when I first read effective java-item31.

 

The phenomenon:

Why we can’t use float and double to calculate decimal when exact results are required?

Try the following statements:

 

System.out.println( 1.03   -  . 42 ); // the answer is 0.6100000000000001 !

System.out.println(
1.00   -   9 * . 10 ); // the answer is 0.09999999999999995 !

 

You will find the answers are not agree with what you have learned in primary school.

 

The reason:

It is all because float and double can not represent exactly 0.1, or any other negative power of ten. They are designed for scientific and engineering calculation which demands accurate approximation. And they are ill-suited for monetary calculations.

 

The solution:

Use BigDecimal, int or long instead.

 

 

BigDecimal bd  =   new  BigDecimal( " 1.00 " );

 

 

Transfer all decimal numbers involved in the calcution into BigDecimal objects as above, and then use them to calcute for a exact result, following with two disadvantages, the first, obviously, less convenient than using primitive types, all notations like +,-,*,/ can not be applied neither; the second, it will be slower, which can be ignored if it was not used in a heavily repeated loop. But you also get one merit which comes from the fact that BigDecimal carrys quite a lot of rounding methods.

If the quantity is not big, you can use int or long instead, you surely understand how to implement the idea. In performance critical section, and you don’t mind keeping track the of the decimal point yourself, this is feasible. But you have not choice if the quantity is over 18 digits, only BigDecimal is available.

posted on 2006-09-13 19:23 Ye Yiliang 阅读(8703) 评论(4)  编辑  收藏 所属分类: Java

FeedBack:
# re: 不要用float和double来进行精确的小数计算 2006-09-14 00:21 weide
从写Dephi的时候就发现了这个问题,尤其在比较大小的时候让人相当困惑

然后用C#写程序的时候,开始全用了Decimal类型,但是这个计算过程也是相当的繁琐,所有数据在运算之间都得转成double,因为.net提供的数学函数的参数都是double(或者有不是的,我还没找到)

后来又花了很大的功夫全变成double了,惨啊-__-|||

另外就是如何确定有效位数的问题,如果不做取舍,常常会搞得数据超长,当参与计算的数据多起来的时候,就不知道多少是有效的数据了:(

楼主可有解决办法?  回复  更多评论
  
# re: 不要用float和double来进行精确的小数计算 2006-09-14 08:42 Flyingis
@weide
Decimal的构造方法应该可以直接设定有效位数啊。

关于这个问题,在《Java Puzzlers》上面也有详细的阐述。  回复  更多评论
  
# re: 不要用float和double来进行精确的小数计算 2006-09-14 13:06 lgn21st
《Java Puzzle》上面开篇就提到这个puzzle!  回复  更多评论
  
# re: 不要用float和double来进行精确的小数计算 2006-09-14 18:54 冰川
没错!
金融方面的软件一般都用BigDecimal.  回复  更多评论
  

只有注册用户登录后才能发表评论。


网站导航: