双精度浮点型 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()方法。