乐途乐途
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
  • 学习路径
  • 第1章 Java概述与环境搭建

    • Java概述与环境搭建
    • Java语言概述
    • 解释型语言与编译型语言对比
    • JDK安装与配置
    • JDK、JRE、JVM 详解
    • HelloWorld程序详解
    • IDE 介绍
  • 第2章 标识符与基本数据类型

    • 章节导读
    • 变量概述
    • 常量概述
    • 基本类型与包装类
    • 字节型 byte
    • 短整型 short
    • 整型 int
    • 长整型 long
    • 单精度浮点型 float
    • 双精度浮点型 double
    • 字符型 char
    • 布尔型 boolean
    • 类型转换
  • 第3章 运算符与表达式

    • 章节导读
    • 算术运算符
    • 赋值运算符
    • 关系运算符
    • 逻辑运算符
    • 位运算符
    • 条件运算符
    • 运算符优先级
    • 表达式
  • 第4章 流程控制

    • 章节导读
    • 常见的程序运行流程
    • if-else 选择结构
    • switch 多分支选择
    • while 循环
    • do-while 循环
    • for 循环
    • break 与 continue
  • 第5章 数组

    • 章节导读
    • 一维数组
    • 多维数组
    • Arrays 工具类
  • 第6章 类与对象

    • 章节导读
    • 类与对象
    • 方法定义与调用
    • 构造方法
    • 封装
    • 访问修饰符
    • package 与 import
    • static 关键字
    • this 关键字
    • 参数传递 详解
    • 枚举
    • 成员内部类
    • 局部内部类
    • 静态内部类
    • 匿名内部类
  • 第7章 接口与继承

    • 章节导读
    • 继承
    • super 关键字
    • final 关键字
    • 多态
    • 向上转型与向下转型
    • 抽象类
    • 接口
    • 抽象类与接口对比
  • 第8章 注解

    • 章节导读
    • 注解基础
    • 元注解详解
    • 自定义注解
  • 第9章 常用类

    • 章节导读:Java 常用类
    • Object 类:万类之祖
    • 包装类:基本类型的对象化
    • String:不可变的字符串
    • StringBuffer:线程安全的可变字符串
    • StringBuilder:可变的字符串构建器
    • Math:数学运算工具类
    • Random:伪随机数生成器
    • 大数值运算 详解
    • 日期时间API 详解
  • 第10章 异常机制

    • 章节导读
    • 异常体系与分类
    • try-catch-finally
    • try-with-resources
    • throws 与 throw
    • 自定义异常
  • 第11章 泛型

    • 章节导读
    • 泛型基础
    • 通配符与PECS原则
    • 类型擦除
  • 第12章 集合框架

    • 章节导读
    • 集合框架概述
    • ArrayList
    • LinkedList
    • HashMap 详解
    • LinkedHashMap 详解
    • TreeMap 详解
    • HashSet
    • TreeSet 详解
    • TreeSet 与 Comparable
    • Collections 工具类详解
  • 第13章 IO流

    • 章节导读
    • IO流概述
    • 字节流
    • 字符流
    • 缓冲流
    • 转换流 详解
    • 序列化 详解
    • NIO与Files 详解
    • NIO与Files工具类
  • 第14章 多线程与并发

    • 第十六章 多线程与并发 —— 章节导读
    • 线程基础详解
    • synchronized 详解
    • Lock 与显式锁详解
    • volatile 详解
    • wait 与 notify 详解
    • ThreadLocal详解
    • 原子类详解
    • 并发工具类详解
    • 线程池详解
  • 第15章 反射

    • 章节导读
    • 反射概述与 Class 对象
    • Constructor 与对象创建
    • Field与Method详解
    • 反射应用详解
  • 第16章 JDK8新特性

    • 章节导读
    • Lambda 表达式
    • Stream API 基础
    • Stream API 高级详解
    • Optional 详解
    • 新日期时间API详解
  • 第17章 JDK9-11新特性

    • 章节导读
    • 模块化系统 — Project Jigsaw(JDK 9)
    • var 局部变量类型推断(JDK 10)
    • 集合工厂方法与增强(JDK 9 / 10 / 11)
    • 接口增强:private 方法(JDK 9)
    • Stream API 增强(JDK 9)
    • Optional 增强(JDK 9 / 10 / 11)
    • String 新增方法(JDK 11)
    • HTTP Client 与 Files 增强(JDK 11)
    • 直接运行 Java 源文件 — JEP 330(JDK 11)
  • 第18章 JDK12-17新特性

    • 章节导读
    • Switch 表达式(JDK 12 预览 / JDK 14 正式)
    • 文本块 Text Blocks(JDK 13 预览 / JDK 15 正式)
    • Records 记录类(JDK 14 预览 / JDK 16 正式)
    • 密封类 Sealed Classes(JDK 15 预览 / JDK 17 正式)
    • instanceof 模式匹配(JDK 14 预览 / JDK 16 正式)
    • Switch 模式匹配 — Pattern Matching for switch(JDK 17 预览 / JDK 21 正式)
    • Helpful NPE 与 String 增强(JDK 12 / JDK 14 / JDK 15)
    • Stream 增强(JDK 12 / JDK 16)
    • 日期时间增强 — Day Period 支持(JDK 16)
  • 第19章 JDK18-21新特性

    • 章节导读
    • 虚拟线程(JDK 19 预览 / JDK 20 第二预览 / JDK 21 正式)
    • 序列集合(JDK 21 正式)
    • Switch 模式匹配(JDK 17 预览 / JDK 18 第二预览 / JDK 20 第四预览 / JDK 21 正式)
    • Record 模式匹配(JDK 19 预览 / JDK 20 第二预览 / JDK 21 正式)
    • 未命名模式与变量(JDK 21 预览 / JDK 22 正式)
  • 第20章 JDK 22-25 新特性

    • 章节导读
    • 字符串模板(JDK 22 预览 / JDK 23 第二预览 / JDK 24 第三预览)
    • Stream Gatherers(JDK 22 预览 / JDK 24 第二预览)
    • 隐式声明类与实例方法(JDK 23 预览 / JDK 24 第二预览)
    • 原始类型模式匹配(JDK 24 预览)
  • 附录

    • Java 核心知识点
    • Java SE 专业术语
    • Java特性索引(JDK 8 → 25)

双精度浮点型 double

本章定位:double是64位IEEE 754双精度浮点数,有效数字约15~16位十进制数,是Java浮点数的默认类型。在飞翔科技的系统中,圆周率3.141592653589793、地月距离384400.12345、Math.sqrt(2)等科学计算场景都用double。大翔强调:"能用double就别用float——除非你有内存或GPU的特殊限制。"


定义与语法

属性说明
关键字double(双精度浮点型)
位数64 bits(8字节)
标准IEEE 754 双精度浮点数
有效数字约15~16位十进制数字
取值范围(正数)约 4.9×10⁻³²⁴ ~ 1.8×10³⁰⁸
默认值0.0d
包装类java.lang.Double
字面量后缀可选 d 或 D(浮点字面量默认就是double)

基本声明

double pi = 3.141592653589793;       // 圆周率
double moonDistance = 384400.12345;  // 地月距离(km)
double sqrt2 = Math.sqrt(2);         // √2
double drugHalfLife = 3.141592653589793;  // 药物半衰期(π小时)
double discountRate = 3.141592653589793 / 10;  // 31.4%促销折扣
double goldenRatio = 1.61803398875;  // 黄金分割

深度原理:double vs float 的精度提升

double的64位布局

计算公式:

value = (-1)^S × (1.M) × 2^(E-1023)

精度对比:
- float:  2^-23 ≈ 1.19×10⁻⁷  (约7位有效数字)
- double: 2^-52 ≈ 2.22×10⁻¹⁶ (约16位有效数字)

精度对比实验

float  表示 3.141592653589793 → 3.1415927  (精度损失在第8位)
double 表示 3.141592653589793 → 3.141592653589793 (15位精确)

完整代码示例

示例一:飞翔旅游的地月距离计算

/**
 * 飞翔科技 - double 类型演示
 * 场景:飞翔旅游推出的"地月星际旅行"项目
 */
public class DoubleMoonDemo {
    public static void main(String[] args) {
        // 地月距离
        double moonDistance = 384400.12345;          // km
        double pi = 3.141592653589793;
        double sqrt2 = Math.sqrt(2);
        
        // 假设旅行速度:光速的万分之一(29.98 km/s)
        double lightSpeed = 299792.458;               // km/s
        double travelSpeed = lightSpeed / 10000;      // 29.98 km/s
        double travelTimeSeconds = moonDistance / travelSpeed;
        double travelTimeHours = travelTimeSeconds / 3600;
        
        // 月球周长计算(月球半径约1737.1km)
        double moonRadius = 1737.1;
        double moonPerimeter = 2 * pi * moonRadius;
        
        System.out.println("【飞翔星际旅行 - 地月航线】");
        System.out.println("地月距离:" + moonDistance + " km");
        System.out.println("航行速度:" + String.format("%.2f", travelSpeed) + " km/s");
        System.out.println("预计航行时间:" + String.format("%.2f", travelTimeHours) + " 小时");
        System.out.println("月球周长:" + String.format("%.2f", moonPerimeter) + " km");
        
        System.out.println("\n【数学常数】");
        System.out.println("π = " + pi);
        System.out.println("√2 = " + sqrt2);
        System.out.println("黄金分割比:" + (1 + Math.sqrt(5)) / 2);
    }
}
运行输出:
【飞翔星际旅行 - 地月航线】
地月距离:384400.12345 km
航行速度:29.98 km/s
预计航行时间:3.56 小时
月球周长:10914.73 km

【数学常数】
π = 3.141592653589793
√2 = 1.4142135623730951
黄金分割比:1.618033988749895

示例二:double的精度极限测试

/**
 * 飞翔科技 - double 精度极限演示
 * 场景:白歌让小崔理解double的精度边界
 */
public class DoublePrecisionDemo {
    public static void main(String[] args) {
        // 精度测试:逐渐增加小数位数
        double pi = 3.141592653589793;
        System.out.println("π (double): " + pi);
        System.out.println("π (15位):    3.141592653589793");  // 全对
        System.out.println("π (16位):    3.1415926535897932"); // 最后一位有误差
        
        // 大数 + 小数的精度丢失
        double bigNumber = 1.0e15;     // 1000000000000000
        double smallNumber = 0.1;
        double sum = bigNumber + smallNumber;
        System.out.println("\n【大数吃小数】");
        System.out.println("1e15 + 0.1 = " + sum);
        System.out.println("是否等于1e15?" + (sum == bigNumber));  // true!小数被吃了
        
        // 极小值
        System.out.println("\n【double 极值】");
        System.out.println("MIN_VALUE: " + Double.MIN_VALUE);         // 4.9E-324
        System.out.println("MAX_VALUE: " + Double.MAX_VALUE);         // 1.7976931348623157E308
        System.out.println("MIN_NORMAL: " + Double.MIN_NORMAL);       // 2.2250738585072014E-308
        
        // 特殊值
        System.out.println("\n【特殊值】");
        System.out.println("1.0/0.0 = " + (1.0 / 0.0));            // Infinity
        System.out.println("0.0/0.0 = " + (0.0 / 0.0));            // NaN
        System.out.println("Math.sqrt(-1) = " + Math.sqrt(-1.0));  // NaN
    }
}
运行输出:
π (double): 3.141592653589793
π (15位):    3.141592653589793
π (16位):    3.1415926535897932

【大数吃小数】
1e15 + 0.1 = 1.0000000000000001E15
是否等于1e15?false

【double 极值】
MIN_VALUE: 4.9E-324
MAX_VALUE: 1.7976931348623157E308
MIN_NORMAL: 2.2250738585072014E-308

【特殊值】
1.0/0.0 = Infinity
0.0/0.0 = NaN
Math.sqrt(-1) = NaN

易错场景

反例一:用double做精确比较

// ❌ 错误:浮点数直接比较
double a = 0.1 + 0.2;
double b = 0.3;
if (a == b) {  // 条件为 false!
    System.out.println("相等");
}
System.out.println(a);  // 0.30000000000000004
System.out.println(b);  // 0.3

// ✅ 正确:误差容限比较
double epsilon = 1e-10;
if (Math.abs(a - b) < epsilon) {
    System.out.println("在误差范围内相等");
}

反例二:"大数吃小数"的精度陷阱

// ❌ 问题:大数 + 极小数,小数被"吃掉"
double nationalDebt = 1.0e14;   // 100万亿
double myMoney = 0.01;
double total = nationalDebt + myMoney;
// total 可能等于 nationalDebt,myMoney 完全丢失

// ✅ 认知:double 只有约16位有效数字
// nationalDebt = 1.0 × 10^14 有14位整数位
// myMoney = 1.0 × 10^-2 太小了,加上去被舍入掉了
// 解决方案:如果确实需要这种精度,用 BigDecimal

反例三:用double做货币运算

// ❌ 危险:double做货币累加会累积误差
double balance = 1.00;
for (int i = 0; i < 10; i++) {
    balance -= 0.10;
}
System.out.println(balance);  // 输出 1.3877787807814457E-16(不精确!)

// ✅ 正确:货币运算使用 BigDecimal
import java.math.BigDecimal;
BigDecimal bd = new BigDecimal("1.00");
for (int i = 0; i < 10; i++) {
    bd = bd.subtract(new BigDecimal("0.10"));
}
System.out.println(bd);  // 输出 0.00(精确)

float vs double 选择指南


面试考点

Q1:double为什么是浮点数的默认类型?

Java设计者们认为double的64位精度(15位有效数字)能覆盖绝大多数应用场景,且在现代硬件上double运算速度与float相当(甚至更快,因为很多CPU的浮点单元原生支持64位)。将double设为默认避免了隐式精度损失,使用者需要显式加f后缀来表明"我可以接受较低精度"。

Q2:double d = 1.0 - 0.9; 结果是多少?

结果约等于0.09999999999999998,而不是精确的0.1。因为在二进制中,0.9是个无限循环小数,double只能存储其近似值。这是IEEE 754标准的固有限制,所有主流编程语言的浮点数都有此问题。

Q3:什么是"大数吃小数"?如何避免?

当两个数量级相差很大的double相加时,较小的数可能被完全舍入掉(因为double只有52位尾数,无法同时表示大数的整数部分和小数的精细部分)。例如1e16 + 1.0 = 1e16。避免方法:①重新组织算法,先加小数再加大数;②使用Kahan求和算法补偿丢失的精度;③改用BigDecimal。

Q4:Double.NaN == Double.NaN 的结果是什么?为什么?

结果是false。IEEE 754规定NaN与任何值(包括自身)比较都返回false。这是为了保持"NaN表示未定义/无效运算结果"的语义——两个未定义的结果不能认为相等。判断NaN应使用Double.isNaN()方法。

上一页
单精度浮点型 float
下一页
字符型 char