9 Double 与 Float —— IEEE 754 浮点数在 Java 中的实现

Double 与 Float ------ IEEE 754 浮点数在 Java 中的实现

适用版本: JDK 8 难度等级: 进阶 核心概念: IEEE 754 标准、NaN 与无穷大、位布局、精度损失


一、IEEE 754 浮点数标准概要

Java 的 floatdouble 严格遵循 IEEE 754 标准。理解这个标准是掌握浮点包装类的关键。

c 复制代码
double (64 位) 的位布局:

 63        62 ─────────── 52  51 ───────────────────────── 0
┌────┐┌───────────────────┐┌────────────────────────────────┐
│sign│      exponent      │           mantissa              │
│ 1  │      11 bits       │           52 bits               │
└────┘└───────────────────┘└────────────────────────────────┘

float (32 位):

 31     30 ──────── 23    22 ──────────────────────── 0
┌────┐┌───────────────┐┌─────────────────────────────┐
│sign│    exponent    │           mantissa           │
│ 1  │     8 bits     │           23 bits            │
└────┘┘───────────────┘└─────────────────────────────┘

二、Double 的核心属性

java 复制代码
public final class Double extends Number implements Comparable<Double> {

    // 正无穷大: 指数全1,尾数全0,符号位0
    public static final double POSITIVE_INFINITY = 1.0 / 0.0;
    // 负无穷大: 指数全1,尾数全0,符号位1
    public static final double NEGATIVE_INFINITY = -1.0 / 0.0;
    // NaN: 指数全1,尾数非0
    public static final double NaN = 0.0d / 0.0;

    public static final double MAX_VALUE = 0x1.fffffffffffffP+1023;
    public static final double MIN_NORMAL = 0x1.0p-1022;
    public static final double MIN_VALUE = 0x0.0000000000001P-1022;

    public static final int MAX_EXPONENT = 1023;
    public static final int MIN_EXPONENT = -1022;

    public static final int SIZE = 64;
    public static final int BYTES = SIZE / Byte.SIZE;

    public static final Class<Double> TYPE
        = (Class<Double>) Class.getPrimitiveClass("double");

    private final double value;
}

三、特殊值判断方法

java 复制代码
// NaN 的判定------利用了 NaN != NaN 的性质
public static boolean isNaN(double v) {
    return (v != v);
}

public static boolean isInfinite(double v) {
    return (v == POSITIVE_INFINITY) || (v == NEGATIVE_INFINITY);
}

public static boolean isFinite(double d) {
    return Math.abs(d) <= Double.MAX_VALUE;
}
java 复制代码
public class SpecialValuesDemo {
    public static void main(String[] args) {
        double nan = Double.NaN;
        double inf = Double.POSITIVE_INFINITY;

        System.out.println("NaN != NaN: " + (nan != nan));           // true
        System.out.println("isNaN: " + Double.isNaN(nan));           // true
        System.out.println("isFinite(NaN): " + Double.isFinite(nan)); // false
        System.out.println("isInfinite(inf): " + Double.isInfinite(inf)); // true

        // NaN 的哈希码是固定值
        System.out.println("NaN hashCode: " + Double.hashCode(nan));
    }
}

四、doubleToLongBits ------ 位布局解析

java 复制代码
public static long doubleToLongBits(double value) {
    long result = doubleToRawLongBits(value);
    // 将所有 NaN 规范化为同一个"规范 NaN"值
    if (((result & DoubleConsts.EXP_BIT_MASK) == DoubleConsts.EXP_BIT_MASK) &&
        (result & DoubleConsts.SIGNIF_BIT_MASK) != 0L) {
        result = 0x7ff8000000000000L;
    }
    return result;
}

// 原生方法,不做 NaN 规范化
public static native long doubleToRawLongBits(double value);

五、浮点数精度问题

java 复制代码
public class PrecisionDemo {
    public static void main(String[] args) {
        // 经典案例1: 0.1 无法精确表示
        System.out.println(0.1 + 0.2);          // 0.30000000000000004
        System.out.println(0.1 + 0.2 == 0.3);   // false

        // 经典案例2: 浮点数比较应使用容差
        double a = 0.1 + 0.2;
        double b = 0.3;
        double epsilon = 1e-10;
        System.out.println(Math.abs(a - b) < epsilon);  // true

        // 经典案例3: BigDecimal 用于精确计算
        java.math.BigDecimal bd1 = new java.math.BigDecimal("0.1");
        java.math.BigDecimal bd2 = new java.math.BigDecimal("0.2");
        java.math.BigDecimal bd3 = bd1.add(bd2);
        System.out.println(bd3);  // 0.3------精确
    }
}

六、compare 的特殊逻辑

java 复制代码
public static int compare(double d1, double d2) {
    if (d1 < d2) return -1;
    if (d1 > d2) return 1;

    long thisBits = Double.doubleToLongBits(d1);
    long anotherBits = Double.doubleToLongBits(d2);

    return (thisBits == anotherBits ? 0 :
            (thisBits < anotherBits ? -1 : 1));
}

这个实现确保了:

  • -0.0 < 0.0(-0.0 的 longBits 是 0x8000000000000000L,比 0.0 的 0L 大)
  • NaN 之间视为相等(规范化为相同的 longBits)
  • NaN > 任何非 NaN 值

七、Float 的特殊之处

Float 与 Double 高度类似但有几个关键差异:

特征 Double Float
位数 64 32
指数位 11 8
尾数位 52 23
SIZE 64 32
精度 ~15 位十进制 ~7 位十进制

八、综合实战:高精度计算器

java 复制代码
import java.math.BigDecimal;
import java.math.RoundingMode;

public class PreciseCalculator {

    public static double add(double a, double b) {
        return BigDecimal.valueOf(a).add(BigDecimal.valueOf(b)).doubleValue();
    }

    public static double subtract(double a, double b) {
        return BigDecimal.valueOf(a).subtract(BigDecimal.valueOf(b)).doubleValue();
    }

    public static double multiply(double a, double b) {
        return BigDecimal.valueOf(a).multiply(BigDecimal.valueOf(b)).doubleValue();
    }

    public static double divide(double a, double b, int scale) {
        return BigDecimal.valueOf(a)
            .divide(BigDecimal.valueOf(b), scale, RoundingMode.HALF_UP)
            .doubleValue();
    }

    public static String formatScientific(double value, int decimals) {
        if (Double.isNaN(value)) return "NaN";
        if (Double.isInfinite(value))
            return value > 0 ? "+Infinity" : "-Infinity";
        return String.format("%." + decimals + "e", value);
    }

    public static void main(String[] args) {
        System.out.println("0.1 + 0.2 = " + add(0.1, 0.2));  // 0.3
        System.out.println("10 / 3 = " + divide(10, 3, 4));    // 3.3333

        double huge = 1e308;
        System.out.println("科学计数: " + formatScientific(huge, 2));
    }
}

九、面试要点

问题 关键要点
为什么 0.1+0.2 != 0.3 0.1 和 0.2 在二进制中都是无限循环小数
NaN 判断为何用 v != v NaN 是唯一不等于自身的值
-0.0 和 0.0 的区别 位表示不同,compare 中 -0.0 < 0.0
doubleToLongBits vs doubleToRawLongBits 前者规范化 NaN,后者保留原始位
相关推荐
Refrain_zc4 小时前
Android 二维码登录轮询机制:从扫码到登录的完整客户端实现
java
z落落4 小时前
C#参数区别
java·算法·c#
日月云棠4 小时前
5 StringBuffer —— 线程安全的可变字符串
java·后端
happymaker06264 小时前
SpringBoot学习日记——DAY06(整合MyBatisPlus的其他功能)
java·spring boot·学习
砍材农夫5 小时前
物联网 基于netty核心实战-会话管理
后端
元宝骑士5 小时前
MySQL 8.0 递归 CTE:树形结构一键生成层级 Path 并更新回表
后端·mysql
Refrain_zc5 小时前
Android 播放器进度条改造实践:句级音频列表映射秒级时间轴
java
我命由我123455 小时前
Bugly - Bugly 基本使用( App 质量追踪平台)
android·java·java-ee·android studio·android jetpack·android-studio·android runtime
宋哥转AI5 小时前
Spring AI Graph:从0到Supervisor(一)RAG子图+Supervisor路由踩坑全记录
java·agent