Java学习打卡-Day5-浮点数精度丢失问题、BigDecimal、重写与重载

学习自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)
    • 同一个类中同名方法的不同实现
    • 参数列表不同(参数类型,参数个数甚至是参数顺序不同都算)。
    • 不能通过返回类型是否相同来判断重载
  • 区别:
    • 方法的重载和重写都是实现多态的方式,区别在于重载实现的是编译时的多态性,而重写实现的是运行时的多态性。重载发生在一个类中,同名的方法如果有不同的参数列表(参数类型不同、参数个数不同或者二者都不同)则视为重载;重写发生在子类与父类之间,重写要求子类被重写方法与父类被重写方法有相同的参数列表,有兼容的返回类型,比父类被重写方法更好访问,不能比父类被重写方法声明更多的异常(里氏代换原则)。重载对返回类型没有特殊的要求,不能根据返回类型进行区分。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值