float为啥不准确

164 阅读2分钟

浮点数

任何一个10进制数 N 都可以表示为

`N = X \* 10^Z`

比如

`314.15=3.1415\*10^2`

其中 X 被称为尾数,Z 被称为阶码,这里的10 是底数。 尾数的长度决定了浮点数的精度。

IEEE 754 定义的浮点数

IEEE 754 规定了计算机中32位浮点数表示方法。我们先看一下32位浮点数在内存中的结构分布:

image.png

  • 最高位表示数据的符号:0表示正数,1表示负数
  • 最低位23位表示尾数,并且在还原回真正浮点数尾数时应为1.x
  • 中间8位表示阶码,使用移码表示
  • 规定底数为2

举例如下

image.png

  1. 符号位1表示负数

  2. 中间8位阶码换算源码 00001011(移码)->10001011(补码)->10001010(反码)->11110101(原码)=-117

    • 原码:最高位符号位,剩下n-1表绝对值,二进制
    • 反码:在原码的基础上,除了符号位不变,其他位取反
    • 补码:在反码的基础上正常的加法计算加1
    • 移码:将补码的符号位取反

    尾数部分隐含了最前面整数的1,因此,阶码应当作出修正来适应隐藏操作,即阶码需要加1,所以,最终的阶码应当为-116

  3. 尾数101,最终位0.5+0.125=0.625,加1位1.625

最终为-1.625*2^-116

最终原因

Java中float类型的格式基本遵循IEEE754标准。 尽管数学意义上的小数是连续的,但float仅仅能表示其中的一些离散点,把这些离散点组成的集合记为S,S的大小还是有限的。

如果要保存的小数P刚好在集合S内,那么float类型就能精确的表示P;否则float类型只能从集合S中找一个与P最近的离散点P'代替P。