码迷,mamicode.com
首页 > 编程语言 > 详细

Java中四则运算的那些坑

时间:2015-06-09 14:15:11      阅读:188      评论:0      收藏:0      [点我收藏+]

标签:

使用Java开发多年,感觉自己的水平也在不断提升,但是被Java狂虐却从来都没变过,而且任何一个Java的小角落,都能把我虐的体无完肤,但是无奈要靠Java吃饭,还得恬着脸继续使用下去。
说说最近遇到的问题,则于新工作属于互联网金融,所以里面涉及到了大量的资金计算,资金计算对数字要求的比较严谨,作为一个粗心而又自大的Java程序员,一直没把这个当回事儿,于是又被Java吊打一遍。
下面记录一下Java中四则运算的一些需要注意的小坑。

数学计算,免不了要想到 int long double 这种数据类型,但double自古在程序界就特立独行,表面上看像数学中学到的小数,实际上完全两码事儿。下面趟一下:

Double的坑

加法坑

double d1 = 0.1;
d1 += 0.1;d1 += 0.1;d1 += 0.1;d1 += 0.1;d1 += 0.1;d1 += 0.1;d1 += 0.1;d1 += 0.1; // 连着加了8次
System.out.println(d1);  

0.8999999999999999

不是我们期望的0.9

减法坑

double d1 = 1.0;
double d2 = 0.1;
d1 -= d2;d1 -= d2;d1 -= d2;d1 -= d2;d1 -= d2;d1 -= d2;d1 -= d2;d1 -= d2;d1 -= d2; // 连着减了9次0.1
System.out.println(d1);  

0.10000000000000014

不是我们期望的0.1

乘法坑

double d1 = 123213.0;
d1 *= 0.35;
System.out.println(d1);

43124.549999999996

不是我们期望的43124.55

除法坑

double result = 11.4/12;
System.out.println(result);  

0.9500000000000001

不是我们期望的0.95

** 由此可见,如果需要对资金进行加减乘除四则运算,千万不能使用double类型的,结果总是出你意料。**

大数转为字符串坑

再试个别的,数学运算,总会使用比较大的数字吧,试一个上千万的数字吧:

double bigValue = 12345678.9;
System.out.println(bigValue);  

1.23456789E7

竟然变成科学计数法了… … 这不是给人看的格式啊。
所以,如果你想要显示大的浮点数,请使用DecimalFormat自己来格式化。

除0坑

从小学数学就知道不能除以0,看看double的表现:

result = 1.0/0.0;
System.out.println(result);
result = 0.0/0.0;
System.out.println(result);  

Infinity
NaN

确实不能除以0,但是结果出乎你的意料之外吧,没错,Double提供了几个方法来检查:
Double.isInfinite(double) // 是否是Infinite数
Double.isFinite(double) // 是否有有限的数
Double.isNaN(double) // 是否是这个数

NaN = Not -a-Number ,两个NaN的数一比较,竟然不相等,是不是越来越有意思了。

比较是否相等的坑

System.out.println(1.1 == 1.1? "true": "false");
System.out.println(1.1 == 1.100000000000001? "true": "false");
System.out.println(1.1 == 1.10000000000000009? "true": "false");  

true
false
true

又出乎意料了

总结:
在进行资金相关的运算时,千万不能使用Double类型,否则一个不小心就让你好看。
那么不使用double,使用哪个类型合适呢?Java提供了一个叫BigDecimal的对象,专门用来做计算使用,但是BigDecimal也不是那么容易驾驭的,使用不当,还是会被爆出翔。

附:浮点数在计算机中的表示方法,提示Double为何如此诡异。

BigDecimal的坑

坑1:实例化对象

BigDecimal num = new BigDecimal(0.3);
System.out.println(num);

0.299999999999999988897769753748434595763683319091796875

这不是玩人嘛,换一种方法:

BigDecimal num = new BigDecimal("0.3");
System.out.println(num);

0.3

这总算可以了。

坑2:除法

BigDecimal d = BigDecimal.ONE;  
d.divide(new BigDecimal("3"));  

Exception in thread “main” java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.

这是什么鬼?为毛报个异常啊…
好吧,保留两位小数试试

BigDecimal d = BigDecimal.ONE;  
d.divide(new BigDecimal("3"), 2, BigDecimal.ROUND_HALF_UP);
System.out.println(d);

1

咦,为何不是0.33,数没变呢?
原因是:BigDecimal是不可变的,每次运算后返回新的对象,再改:

BigDecimal d = BigDecimal.ONE;  
d = d.divide(new BigDecimal("3"), 2, BigDecimal.ROUND_HALF_UP);
System.out.println(d);

0.33

长出一口气,总算可以了。

坑3:小数部分舍入进位的坑

四舍五入,有啥可说的?还真有得说,舍入进位可以参看RoundingMode枚举,里面包含各种舍入进位,需要仔细研读哦,否则一脚一个坑。
这里要说的不是舍入进位的类型,而是说BigDecimal中提供的方法:
num.setScale(2, BigDecimal.ROUND_HALF_EVEN);
num.setScale(2, RoundingMode.HALF_EVEN);
这里面 BigDecimal.ROUND_HALF_EVEN 和 RoungingMode.HALF_EVEN 这两个东西有啥区别?
答案是没有区别,一样使用,RoungingMode 枚举是JDK1.5以后,Java支持枚举类型后加的,里面有一个oldMode,保存的就是BigDecimal中对应的常量。

坑4:保留位数的坑

将小数部分保留N位这是一个非常常用的操作,以前使用double时,都是采用:
1. 乘10的N次方
2. round
3. 除以10的N次方
这种方法来算,牛逼闪闪的BigDecimal必然会提供舍入方法,而且使用起来很简单:

BigDecimal num = new BigDecimal("1.2345678");
num = num.setScale(2, BigDecimal.ROUND_HALF_EVEN);
System.out.println(num);  

1.23

事情本来很容易结束了,但是眼睛贱又扫了一眼别的地方,MathContext,这是啥?好像也是用来舍入的,试试:

MathContext ctx = new MathContext(2);
num = new BigDecimal("12.34");
num1 = new BigDecimal("56.78");
num = num.multiply(num1, ctx);  // 12.34*56.78 = 700.6652
System.out.println(num.toString()); 

7.0E+2

不是我们想看到700.67呀,改一下

MathContext ctx = new MathContext(3);

701

MathContext ctx = new MathContext(4);

700.7

MathContext ctx = new MathContext(5);

700.67

MathContext ctx = new MathContext(6);

700.665

明白了,这个数字是所有数字的位数,而不是小数点后的位数。

坑5:比较相等的坑

num = new BigDecimal("1");
num1 = new BigDecimal("1.0");
System.out.println(num.equals(num1)); // 竟然不相等呀
System.out.println(num.compareTo(num1)); // 0   

false
0

BigDecimal的equals大大出乎意料,可不能随便使用equals方法来比较两个BigDecimal的大小了。还好compareTo还是好用的… …

Java中四则运算的那些坑

标签:

原文地址:http://my.oschina.net/gccr/blog/464556

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!