Java数值加减乘除结果都是对的么?丨【奔跑吧!JAVA】

网友投稿 1282 2022-05-30

计算机程序内部都是二进制表示的,日常的编程实战中,除了处理字符串外,大部分情况下,都在处理数值的计算,如计算多个值的和或平均值。在Java语言体系中,关于数值的类型有int,float,double,long等。

对于有小数的数值,开发人员首先想到的是可以用float或者double表示。比如求1.0和0.42两个值的差,那么心算可得0.58,但是如果用Java来实际计算一下,那么其结果可能和我们预期的是不一样的。

Java中数值的加减乘除结果都是对的么?丨【奔跑吧!JAVA】

下面结合特殊场景,来给出Java常见的数值加减乘除运算示例,看看结果都是对的么?具体的示例代码如下:

package com.jyd; //Java 数值计算精度问题探讨示例 public class NumDemo { public static void main(String[] args) { int a = 10; int c0 = a / 3 ; System.out.println(c0);//3,只保留整数 //float和double大部分情况精确的,但有时会计算错误 // 在商业计算上(如金融、银行,财务),不建议使用。 float b = 0.1f; float c2 = b / 3.0f;//0.033333335 System.out.println(c2); float b2 = 20019999; System.out.println(b2); //2.002E7 double d = 45.9D; double c3 = d / 3.0D ; System.out.println(c3);//15.299999999999999 (not 15.3) double e = 2001299.32D; double e2 = 0.11D; System.out.println(e + e2); //2001299.4300000002 System.out.println(1.0 - 0.42);//0.5800000000000001 System.out.println(4.015 * 100);//401.49999999999994 System.out.println(0.01 + 0.05);//0.060000000000000005 } }

上述示例代码中,int类型的数值10,/ 除以3,实际返回的是int类型,即3,只保留整数部分。这个计算结果只要我们知道,一般都可以理解。Java中的浮点类型(如float和double)采用 IEEE 754 标准。float类型的值0.1除以3.0时,返回的还是float类型的值,按照数学角度来说,应该返回0.3333...(循环),但是Java程序返回0.033333335。float类型长度为32位,其中用 1 位表示数字的符号,用 8 位表示指数,用 23 位表示小数部分。感觉这个长度有的长,但是实际使用起来精度还是不够。即使是64位的double也是如此。

示例中,20019999这个float类型的值,输出后却是2.002E7,即20020000,真是奇乎怪哉!,如果这个是银行账户的金额,1个账户多1元,那么几亿个户头则多出了几个亿的金额,足见这个计算精度的问题是在特殊场景下,是一个不可接受的方案。 其他的示例涉及到加法、减法、乘法,也同样会出现计算错误的问题。

按照官网的说法,float和double一般用于工程计算,而不能应用商业计算,如银行,财务等。那么商业计算上,该如何解决此问题呢?此时需要用到一个特殊的类型,为BigDecimal。此类型是一个精确的类型,下面给出示例:

package com.jyd; import java.math.BigDecimal; import java.math.MathContext; //Java 数值计算精度问题探讨示例 public class BigNumDemo { public static void main(String[] args) { BigDecimal b = new BigDecimal("0.1"); BigDecimal b1 = new BigDecimal("3.0"); BigDecimal c2 = b.divide(b1,10,BigDecimal.ROUND_DOWN) ;//0.0333333333 System.out.println(c2); BigDecimal b2 = new BigDecimal(""+20019999); System.out.println(b2); //20019999 BigDecimal d = new BigDecimal("45.9"); BigDecimal d2 = new BigDecimal("3.0"); BigDecimal c3 = d.divide(d2,10,BigDecimal.ROUND_CEILING) ; System.out.println(c3);//15.3000000000 BigDecimal e = new BigDecimal("2001299.32"); BigDecimal e2 = new BigDecimal("0.11"); System.out.println(e.add(e2)); //2001299.43 BigDecimal e3 = new BigDecimal("1.0"); BigDecimal e4 = new BigDecimal("0.42"); System.out.println(e3.subtract(e4));//0.58 BigDecimal e5 = new BigDecimal("4.015"); BigDecimal e6 = new BigDecimal("100"); System.out.println(e5.multiply(e6, MathContext.UNLIMITED));//401.500 BigDecimal e7 = new BigDecimal("0.01"); BigDecimal e8 = new BigDecimal("0.05"); System.out.println(e7.add(e8));//0.06 BigDecimal e9 = new BigDecimal("999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999"); System.out.println(e9.add(e3));//1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0 } }

BigDecimal有多种构造函数,常用的有2种。建议使用String构造方式,不建议使用double构造方式,可能会出现精度丢失问题。通过上述示例可以看出,商业计算应该使用 BigDecimal类型来完成,虽然在加减乘除这块使用起来有点不够简便,但是仍然比较容易理解。且操作字符串,也更容易操作。在乘除计算中,强制需要制定额外的参数,比如明确小数个数,四舍五入的模式等。

最后,Java编程的细节还有很多,比如不同精度的数值的赋值和转换问题,float和double为什么会导致数据精度的丢失等,可以参考官网文档或其他资料。这里不再赘述。

【奔跑吧!JAVA】有奖征文火热进行中:

https://bbs.huaweicloud.com/blogs/265241

Java

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:jQuery 选择器
下一篇:通过 Maven 编译打包 Java + Scala 混合项目
相关文章