• [Java]0.1+0.2不等于0.3 !!一分钱问题与解决方案


    一、原因

    原因很简单,计算机存储和计算数组都是用二进制,
    而大部分小数转二进制的时候,就丢失精度了。
    0.1、0.2、0.3这些小数在二进制里都是循环小数,计算机不可能存储无限循环小数,所以只能截取一部分,导致本身失去精度。
    计算机再用这些有误差的小数进行计算,那误差就更大了。

    互转教程:十进制小数 与 二进制小数 互转


    二、一分钱问题

    假如用float和double进行金钱计算,因为二进制转换误差就很容易出现一分钱问题。

    方案

    方案1、 使用BigDecimal

    BigDecimal a = new BigDecimal("0.1"); //切记!!参数一定要是字符串
    BigDecimal b = new BigDecimal("0.2"); //切记!!参数一定要是字符串
    BigDecimal c = a.add(b);
    System.out.println(c);
    
    • 1
    • 2
    • 3
    • 4

    切记!!
    创建BigDecimal参数一定要是字符串
    如果直接传参浮点数,计算机自动转二进制,同样会有误差!!!

    方案2、 使用long

    1、金钱保存到数据库时,金钱乘1000,字段类型为整型。
    2、计算时就使用整型计算。
    3、显示时金钱/1000。

    long a = 0.1 * 1000;
    long b = 0.2 * 1000;
    long c = a + b;
    System.out.println(c / 1000);
    
    • 1
    • 2
    • 3
    • 4

    三、扩展知识 IEEE-754二进制

    计算机浮点数标准。
    它规定了浮点数的表示、运算和舍入方式等方面的规则。

    1、IEEE-754二进制组成

    类型符号位阶码尾数
    float占1bit占8bit占23bit
    double占1bit占11bit占52bit
    1. 符号位,0表示正数,1表示负数。
    2. 指数,浮点数的大小和符号的指数部分。127偏移量
    3. 尾数,浮点数的小数部分。

    2、十进制float转IEEE-754二进制

    步骤例子0.5例子-12
    1获取【符号位】
    0表示正数,1表示负数。
    0.5是正数,符号位为0-12是负数,符号位为1
    2十进制小数 转 二进制十进制0.5转二进制 0.1十进制-12转二进制 1100.011
    3二进制转科学计数法
    1.xx * 2 指数 2^{指数} 2指数
    1.0 ∗ 2 − 1 1.0 * 2^{-1} 1.021 1.100011 ∗ 2 3 1.100011 * 2^3 1.10001123
    4获取十进制指数值
    127+指数
    127+(-1)=126127 + 3 = 130
    5 【指数值】 十进制 转 二进制
    不满八位前面补0
    126 ->01111110130 -> 10000010
    6【尾数位】
    科学计数法的 xx
    不满23位后面补0
    xx为0
    补零:00000000 00000000 0000000
    xx为100011
    补零:10001100 00000000 0000000
    7拼接 符号位+指数值+尾数位0 10000010 000000000000000000000001 10000010 10001100000000000000000

    我们再用上面步骤计算float 0.1
    请添加图片描述

    1. 0.1获取符号位,0.1正数为0。
    2. 0.1转二进制,0.00011001100110011001101(已失去精度)
    3. 科学计数法, 1.1001100110011001101 ∗ 2 − 4 1.1001100110011001101 * 2^{-4} 1.100110011001100110124
    4. 获取十进制指数值,127 + (-4) = 123
    5. 指数值 转 二进制,123 -> 01111011
    6. 尾数位补零,10011001 10011001 101
    7. IEEE-754二进制:0 01111011 1001100110011001101
  • 相关阅读:
    Stable diffusion的一些参数意义及常规设置
    Spring Cloud Stream绑定器架构解析与开发
    05.jvm常量池02
    高企申报需要的专利数量是多少?
    VScode代码片段自动转图标
    理解case when then else end 的使用,基础概念,建表语句,用例讲解
    线程安全问题与解决思路加锁
    Mavendeploy命令详解
    python基础1
    基于安卓android微信小程序的快递取件及上门服务系统
  • 原文地址:https://blog.csdn.net/malu_record/article/details/133850478