学习自JavaGuide
浮点数精度丢失问题
- 原因:由于二进制自身局限性,导致其无法精确的表示所有十进制小数。
float a = 2.0f - 1.9f; float b = 1.8f - 1.7f; System.out.printf("%.9f",a);// 0.100000024 System.out.println(b);// 0.099999905 System.out.println(a == b);// false - 十进制小数转换成二进制小数采用"乘2取整,顺序排列"的做法。基本思想如下:用2乘以当前十进制小数,然后将乘积的整数部分取出,如果乘积中的小数部分为零或者达到所要求的精度则停止计算。否则再用2乘以余下的小数部分,又得到一个乘积,再将积的整数部分取出,如此进行。
// 0.2 转换为二进制数的过程为,不断乘以 2,直到不存在小数为止, // 在这个计算过程中,得到的整数部分从上到下排列就是二进制的结果。 0.2 * 2 = 0.4 -> 0 0.4 * 2 = 0.8 -> 0 0.8 * 2 = 1.6 -> 1 0.6 * 2 = 1.2 -> 1 0.2 * 2 = 0.4 -> 0(发生循环) ··· - 不是所有十进制小数都能用二进制准确表示,对于这些小数只能根据精度近似表示。
- 浮点数之间的等值判断,基本数据类型不能用 == 来比较,包装数据类型不能用 equals 来判断。正确做法是指定一个误差范围,在此范围内可认为相等;或使用BigDecimal····
如何解决精度丢失问题?
- BigDecimal 可以实现对浮点数的运算,不会造成精度丢失。
- 通常情况下,大部分需要浮点数精确运算结果的业务场景(比如涉及到钱的场景)都是通过 BigDecimal 来做的。
BigDecimal 常见方法
1.创建
- 为了防止精度丢失,推荐使用
BigDecimal(String val)构造方法或者BigDecimal.valueOf(double val)静态方法来创建对象。BigDecimal recommend1 = new BigDecimal("0.1"); BigDecimal recommend2 = BigDecimal.valueOf(0.1);
2.加减乘除
BigDecimal a = new BigDecimal("1.0");
BigDecimal b = new BigDecimal("0.9");
System.out.println(a.add(b));//加法 1.9
System.out.println(a.subtract(b));//减法 0.1
System.out.println(a.multiply(b));//乘法 0.90
System.out.println(a.divide(b));//除法 无法除尽,抛出 ArithmeticException 异常
System.out.println(a.divide(b, 2, RoundingMode.HALF_UP));//推荐除法 1.11
- 使用 divide 方法的时候尽量使用 3 个参数版本,并且RoundingMode 不要选择 UNNECESSARY,否则很可能会遇到 ArithmeticException(无法除尽出现无限循环小数的时候),其中 scale 表示要保留几位小数,roundingMode 代表保留规则。
public BigDecimal divide(BigDecimal divisor, int scale, RoundingMode roundingMode) { return divide(divisor, scale, roundingMode.oldMode); } - 保留规则(roundingMode) :
public enum RoundingMode { // 2.5 -> 3 , 1.6 -> 2 // -1.6 -> -2 , -2.5 -> -3 UP(BigDecimal.ROUND_UP), // 2.5 -> 2 , 1.6 -> 1 // -1.6 -> -1 , -2.5 -> -2 DOWN(BigDecimal.ROUND_DOWN), // 2.5 -> 3 , 1.6 -> 2 // -1.6 -> -1 , -2.5 -> -2 CEILING(BigDecimal.ROUND_CEILING), // 2.5 -> 2 , 1.6 -> 1 // -1.6 -> -2 , -2.5 -> -3 FLOOR(BigDecimal.ROUND_FLOOR), // 2.5 -> 3 , 1.6 -> 2 // -1.6 -> -2 , -2.5 -> -3 HALF_UP(BigDecimal.ROUND_HALF_UP), }
3.保留几位小数
- 通过
setScale()方法。BigDecimal m = new BigDecimal("1.255433"); BigDecimal n = m.setScale(3,RoundingMode.HALF_DOWN); System.out.println(n);// 1.255
4.比较大小
- 使用等值比较(忽略精度):
a.compareTo(b), a 小于 b 返回-1,a 等于 b 返回0 ,a 大于 b 返回1。BigDecimal a = new BigDecimal("1.0"); BigDecimal b = new BigDecimal("0.9"); System.out.println(a.compareTo(b));// 1 equals()方法不仅仅会比较值的大小(value)还会比较精度(scale)。
重写与重载
- 重写(Override)
- 在子类中把父类有的方法重新写一遍。
- 方法名,参数列表,返回类型(除子类中方法的返回值是父类中方法返回值的子类时)都要相同。
- 访问修饰符大于等于父类(public>protected>default>private) 。如果父类方法访问修饰符为 private/final/static ,则子类就不能重写该方法。
- 抛出的异常范围小于等于父类。
- 重载(Overload)
- 同一个类中同名方法的不同实现。
- 参数列表不同(参数类型,参数个数甚至是参数顺序不同都算)。
- 不能通过返回类型是否相同来判断重载。
- 区别:
- 方法的重载和重写都是实现多态的方式,区别在于重载实现的是编译时的多态性,而重写实现的是运行时的多态性。重载发生在一个类中,同名的方法如果有不同的参数列表(参数类型不同、参数个数不同或者二者都不同)则视为重载;重写发生在子类与父类之间,重写要求子类被重写方法与父类被重写方法有相同的参数列表,有兼容的返回类型,比父类被重写方法更好访问,不能比父类被重写方法声明更多的异常(里氏代换原则)。重载对返回类型没有特殊的要求,不能根据返回类型进行区分。

被折叠的 条评论
为什么被折叠?



