1. Java浮点数精度问题深度解析:从float f=3.4说开去
在Java面试中,float f=3.4;这个看似简单的赋值语句背后隐藏着计算机科学中一个经典问题——浮点数精度表示。很多开发者第一次遇到这个问题时都会感到困惑:为什么一个如此基础的赋值操作会存在陷阱?让我们从计算机底层原理出发,彻底弄懂这个面试高频考点。
1.1 IEEE 754浮点数表示标准剖析
Java的float类型遵循IEEE 754单精度浮点数标准,采用32位二进制表示。这32位被划分为三个部分:
- 符号位(1位):0表示正数,1表示负数
- 指数部分(8位):采用偏移码表示,实际指数=存储值-127
- 尾数部分(23位):隐含最高位1(规范化表示)
以3.4为例,其二进制表示过程如下:
-
将3.4转换为二进制科学计数法形式:
- 整数部分3 → 11
- 小数部分0.4 → 0.011001100110011...(无限循环)
- 组合后:11.011001100110011... × 2⁰
-
规范化处理:
- 移动小数点:1.1011001100110011... × 2¹
- 指数部分:1 + 127 = 128 → 10000000
- 尾数部分:10110011001100110011010(23位截断)
最终32位表示为:
code复制0 10000000 10110011001100110011010
1.2 精度丢失的本质原因
当我们将3.4赋值给float变量时,实际发生的过程是:
java复制float f = (float)3.4; // 编译器隐式插入强制类型转换
由于0.4的二进制表示是无限循环小数,而float尾数只有23位存储空间,必须进行截断或舍入。根据IEEE 754的舍入规则(向最接近的值舍入,如果正好居中则向偶数舍入),最终存储的值实际上是:
code复制3.4000000953674316
这就是为什么当我们用BigDecimal打印时看到的是3.4000001192092895——这个差异来自二进制到十进制的转换误差。
关键点:任何不能表示为有限位二进制小数的十进制数,在float/dou
