java中double和float精度丢失问题及解决方法

本文探讨了Java中double和float类型在运算时出现的精度丢失问题,并通过实例展示了解决这一问题的方法——使用BigDecimal类来进行精确计算。

java中double和float精度丢失问题及解决方法

在讨论两位double数0.1和0.2相加时,毫无疑问他们相加的结果是0.2。但是问题总是如此吗?

下面我们让下面两个doubles数相加,然后看看输出结果:

    @Test
    public void testBig(){
        
        System.out.println(0.11+2001299.32);
    }
    

控制台输出2001299.4300000002

我们吃惊的发现,结果并不是我们预想的那样,这是为什么呢?又如何解决呢?

 

现贴出BigDecimal的一个构造函数的文档供大家参考

BigDecimal

public BigDecimal(double val)
将 double 转换为 BigDecimal,后者是 double 的二进制浮点值准确的十进制表示形式。返回的 BigDecimal 的标度是使 (10scale × val) 为整数的最小值。

注:

  1. 此构造方法的结果有一定的不可预知性。有人可能认为在 Java 中写入 new BigDecimal(0.1) 所创建的 BigDecimal 正好等于 0.1(非标度值 1,其标度为 1),但是它实际上等于 0.1000000000000000055511151231257827021181583404541015625。这是因为 0.1 无法准确地表示为 double(或者说对于该情况,不能表示为任何有限长度的二进制小数)。这样,传入 到构造方法的值不会正好等于 0.1(虽然表面上等于该值)。
  2. 另一方面,String 构造方法是完全可预知的:写入 new BigDecimal("0.1") 将创建一个 BigDecimal,它正好 等于预期的 0.1。因此,比较而言,通常建议优先使用 String 构造方法
  3. 当 double 必须用作 BigDecimal 的源时,请注意,此构造方法提供了一个准确转换;它不提供与以下操作相同的结果:先使用 Double.toString(double) 方法,然后使用 BigDecimal(String) 构造方法,将 double 转换为 String。要获取该结果,请使用 static valueOf(double) 方法。

 

解决方法:

相信从上面的文档大家也已经找出了解决方法,

在需要精确的表示两位小数时我们需要把他们转换为BigDecimal对象,然后再进行运算。

另外需要注意

使用BigDecimal(double val)构造函数时仍会存在精度丢失问题,建议使用BigDecimal(String val)

这就需要先把double转换为字符串然后在作为BigDecimal(String val)构造函数的参数。转换为BigDecimal对象

之后再进行加减乘除操作,这样精度就不会出现问题了。这也是为什么有关金钱数据存储都使用BigDecimal。

复制代码
    @Test
    public void testBig(){
        System.out.println(0.11+2001299.32);//非精确的输出
        BigDecimal bigDecimal1 = new BigDecimal(Double.toString(0.11));
        BigDecimal bigDecimal2 = new BigDecimal(Double.toString(2001299.32));
        System.out.println(bigDecimal1.add(bigDecimal2));//精确的输出
    }
复制代码

控制台输出:

2001299.4300000002
2001299.43

### ### 数据类型分类 在 Java 中,`float` `double` 都属于**基本数据类型**中的浮点数类型。其中,`float` 是单精度浮点数,占用 32 位(4 字节),而 `double` 是双精度浮点数,占用 64 位(8 字节)[^1]。 --- ### ### 默认类型与语法差异 Java 中的浮点数字面量默认是 `double` 类型。因此,如果要声明一个 `float` 变量,必须在数值后加上 `f` 或 `F` 后缀。例如: ```java float a = 3.14f; // 必须加 f 后缀 double b = 3.14; // double 可省略后缀 ``` 这种设计是为了避免精度损失问题。如果尝试将一个 `double` 类型的字面量赋值给 `float` 变量而不进行显式转换或添加后缀,则会导致编译错误[^1]。 --- ### ### 精度与存储结构对比 由于 `float` `double` 使用的是 IEEE 754 标准表示法,它们在内存中分别使用不同的位数来表示尾数指数部分,因此其精度也不同: - `float`:有效位数约为 6~7 位十进制数字。 - `double`:有效位数约为 15~16 位十进制数字。 例如,以数值 `8.25` 存储为例,在 `float` `double` 中虽然都能正确表示为二进制形式(即 `1000.01`),但由于 `double` 拥有更高的尾数位数,它能更精确地表示更多小数形式的数据。 --- ### ### 精度丢失问题 在实际使用过程中,`float` `double` 都可能出现精度丢失问题,这是因为它们使用的是**二进制浮点运算机制**,无法准确表示某些十进制小数。例如: ```java public class Test { public static void main(String[] args) { double a = 0.11; double b = 2.32; System.out.println(a + b); // 输出结果可能不等于 2.43 System.out.println(0.11 + 2.32); // 输出结果仍可能不等于 2.43 float c = 0.11f; float d = 2.32f; System.out.println(c + d); // 输出结果也可能存在误差 } } ``` 上述代码中,无论是 `float` 还是 `double`,计算结果都可能存在微小误差,这是由于浮点数在二进制中的近似表示导致的[^2]。 --- ### ### 使用场景对比 | 场景 | `float` 适用性 | `double` 适用性 | |------------------|------------------------|----------------------------| | 科学计算 | ✅(低精度需求) | ✅(高精度首选) | | 图形处理 | ✅(GPU优化场景) | ❌ | | 财务计算 | ❌ | ❌(需用 `BigDecimal`) | | 工程测量 | ✅(如温度采集等) | ✅(如精密仪器数据) | 在需要高精度计算的场景(如金融系统、科学模拟)中,应避免使用 `float` `double`,而推荐使用 `BigDecimal` 类来处理,以确保计算结果的准确性[^1]。 --- ### ### 开发建议 1. **优先选择 `double`**:除非明确需要节省内存或与旧系统交互,否则推荐使用 `double`,因为其精度更高。 2. **避免直接比较相等**:由于精度问题,应使用误差范围判断两个浮点数是否“接近”,例如: ```java boolean isEqual = Math.abs(a - b) < 1e-6; ``` 3. **关键计算使用 `BigDecimal`**:对于涉及金额、税率等关键业务逻辑时,应使用 `BigDecimal` 来避免精度问题[^1]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值