double java 坑_Java中四则运算的那些坑

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

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

Double的坑

坑1:加法坑

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哦

坑2:减法坑

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哦

坑3:乘法坑

double d1 = 123213.0;

d1 *= 0.35;

System.out.println(d1);

43124.549999999996

不是我们期望的43124.55哦

坑4:除法坑

double result = 11.4/12;

System.out.println(result);

0.9500000000000001

不是我们期望的0.95哦

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

坑5:大数转为字符串坑

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

double bigValue = 12345678.9;

System.out.println(bigValue);

1.23456789E7

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

坑6:除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的数一比较,竟然不相等,是不是越来越有意思了。

坑7:比较是否相等的坑

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也不是那么容易驾驭的,使用不当,还是会被爆出翔。

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时,都是采用:

乘10的N次方

round

除以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还是好用的... ...

总结

以上所说的坑,其实在文档中都有所述,但是我们开发时,更多时会依赖以往的经验,这种经验会导致踩到坑里,有时也会踩到屎坑里,所谓细节决定成败,细节决定生死,还是要时刻提醒自己不要大意,注意细节。

最后总结一下: Double在运算时就放弃吧,不要使用了。保存个临时数据时使用还没啥问题,至少我没遇到过。

** BigDecimal要注意:**

1.实例化时使用字符串

2.一定要使用操作后返回值(BigDecimal是不可变的)

3.进行运算前先设置保留几位小数

4.除法运算要在计算时设置保留位数

5.确定要使用哪种进位(四舍五入,还是银行家舍入法)

6.MatchContext不要随便用,根据需要来使用

7.比较两个Decimal是否相等,使用compareTo,不要使用equals

总的来说就是:数学运算需要严谨,Java为了严谨,提供了非常复杂而又麻烦的使用方法,我们还是咬牙凑合用吧。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
import java.io.*; public class Book{ double sb; double xb; Book(double x,double y){ this.sb=x; this.xb=y; } Book(){ } public static void main(String args[]){ System.out.println("请输入数据:"); double a=0; double b=0; double c=0; double d=0; String s; BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); System.out.println("请输入第一个复数的实部:"); try{ s = in.readLine(); a=Double.parseDouble(s); } catch(IOException e) { System.out.println("抛掷异常");} System.out.println("请输入第一个复述的虚部:"); try{ s = in.readLine(); b =Double.parseDouble(s); } catch(IOException e) { System.out.println("抛掷异常");} System.out.println("请输入第二个复述的实部:"); try{ s = in.readLine(); c =Double.parseDouble(s); } catch(IOException e) { System.out.println("抛掷异常");} System.out.println("请输入第二个复述的虚部:"); try{ s = in.readLine(); d =Double.parseDouble(s); } catch(IOException e) { System.out.println("抛掷异常");} Book h; h=new Book(a,b); Book j; j=new Book(c,d); System.out.println("您输入的一个数为:"); toString(h); System.out.println("您输入的二个数为:"); toString(j); Book k; k=new Book(); char z='y'; do{ System.out.println("请选择您要进行的计算:"); System.out.println("1 :进行加法运算"); System.out.println("2 :进行减法运算"); System.out.println("3 :进行修改"); System.out.println("4 :进行乘法运算"); System.out.println("5 :进行除法运算"); System.out.println("6 :查看修改结果"); int i=0; try{ i= Integer.parseInt(in.readLine()); } catch(IOException e) { System.out.println("抛掷异常");} switch(i) { case 1: k.sb=jia(h.sb,j.sb); k.xb=jia(h.xb,j.xb); System.out.println("计算结果的实部为:"+k.sb); System.out.println("计算结果的虚部为:"+k.xb); toString(k); break ; case 2: k.sb=jian(h.sb,j.sb); k.xb=jian(h.xb,j.xb); System.out.println("计算结果的实部为:"+k.sb); System.out.println("计算结果的虚部为:"+k.xb); toString(k); brea

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值