1. 小数
假设你决定编写一个最简单的计算器,或者任何需要进行计算的程序(从最基本的金额统计到复杂的物理计算)。现实生活中并不是所有东西都是整数——这点无可奈何!
所以,让我们掌握一种新的数据类型吧!
在编程中,小数也被称为实数,或浮点数(floating-point)。在 Java 中,与大多数语言一样,它们用于存储不仅是整数,还有各种“带小数”的值:例如 3.14、-28.57、2.718281828……
浮点数主要有两种类型:
| 类型 | 存储 | 值范围(近似) | 精度 | 典型大小 |
|---|---|---|---|---|
|
数字 | ±1.5 × 10-45 ... ±3.4 × 1038 | 小数点后约 7 位 | 4 字节 |
|
数字 | ±5.0 × 10-324 ... ±1.7 × 10308 | 小数点后约 15–16 位 | 8 字节 |
float 类型
float 这个名称源自 floating-point number——浮点数。数学中的“实数”有其严格的性质,而计算机有很多限制。因此在 Java 中把小数称为实数并不十分准确,通常称为“浮点数”。
float 通常可保存 7 位有效数字(例如 0.1234567)、一个 10 的指数,并占用 4 字节内存。对于精确计算来说这太少了,因此大家很快就转向了双精度。
double 类型
double 之名来源于双精度(double)。它在内存中占用 8 字节(是 float 的 2 倍),可保存最多约 15 位有效数字:0.123456789012345。对于大多数涉及小数的计算,这已经足够,因此 double 是 Java 中存储小数的首选类型。
本讲将重点介绍 double:默认情况下,所有“常见”的小数都建议使用它。当然,我们后面也会提到 float。
2. 声明并初始化 double 类型变量
一切都与 int 类似——只是现在使用 double。
// 声明变量并赋值为圆周率
double pi = 3.1415926;
// 也可以只声明而不初始化
double averageSalary;
averageSalary = 91234.56;
// 可以进行运算!
double pizzaPieces = 8;
double friends = 3;
double piecesPerFriend = pizzaPieces / friends; // 2.666...(而不是 2)
语法要点:
- 小数点分隔符使用 点号(3.14)。如果使用逗号——会导致编译错误!
- 严格来说,写成 double d = 3; 并不会报错——类型会自动转换(整数无损地变成“浮点数”)。
3. 使用 Scanner 输入和输出小数
先来尝试输出一个小数:
double amount = 42.75;
System.out.println(amount); // 输出:42.75
很好!如果再加上一些文本:
System.out.println("您的账户余额:" + amount + " 欧元。"); // 您的账户余额:42.75 欧元。
从键盘输入
要读取 double,需要使用 Scanner 的专用方法:console.nextDouble()。
Scanner console = new Scanner(System.in);
System.out.println("请输入室外温度:");
double temperature = console.nextDouble(); // 直接读取为 double
System.out.println("现在室外:" + temperature + " 度。");
4. double 实战:算术运算
所有常见运算(+、-、*、/)与 int 一样工作:
double distance = 100.5;
double time = 2.0;
double speed = distance / time; // 50.25
System.out.println("平均速度:" + speed); // 平均速度:50.25
这就是全部的算术了。唯一的不同在于:只要任一操作数是 double,除法的结果就一定是小数。
与 int 比较
int a = 5, b = 2;
System.out.println(a / b); // 2(余数被丢弃)
double aa = 5, bb = 2;
System.out.println(aa / bb); // 2.5
5. 使用 double 时的常见错误与“怪现象”
输入转换错误
经典场景:用户输入 3,14,而程序期望 3.14。在 Java 中,Scanner.nextDouble() 受当前区域设置(Locale)影响:在俄语/德语区域设置中,逗号可用;在英语区域设置中则需要点号。如有需要,可为 Scanner 设置相应的 Locale,或先读入字符串再手动解析。
// 在 Locale.US 下,如果输入 "3,14" 会出问题
double value = console.nextDouble();
计算机中的数字“非精确性”
这时新手通常会有些困惑:
double x = 0.1 + 0.2;
System.out.println(x); // 咦… 0.30000000000000004
恭喜你,遇到了计算机内部表示小数的“魔法”。许多数字在二进制系统中无法被精确表示。对大多数应用来说这通常无关紧要,但在金融和精密科学中就需要注意这些细节。
6. 重要:double 与 int——自动与显式转换
当你把整数与小数相加,或把 int 赋给 double 变量时——不会报错:
int i = 2;
double d = i; // 一切正常!
System.out.println(d); // 2
double dd = 3.7;
int ii = (int) dd; // 需要显式将 double 转换为 int!
System.out.println(ii); // 3,小数部分被丢弃
这常常令人惊讶:为什么转换之后小数部分没了?原因很简单,类型 int 无法存储小数(小数点后的内容会被直接丢弃)。
关于将 double 转为 int 以及运算符 (int) 的更多内容,将在下一讲中介绍。
7. 格式化输出:优雅地输出 double
默认情况下,double 往往会带一堆没必要的尾随零。我们可以对输出进行格式化:
double temp = 23.56789;
System.out.println(temp); // 23.56789
// 保留 2 位小数
System.out.println(String.format("%.2f", temp)); // 23.57
// 保留 1 位小数
System.out.println(String.format("%.1f%n", temp)); // 23.6
| 格式 | 结果 | 说明 |
|---|---|---|
|
23.57 | 保留 2 位小数的数字 |
|
23.6 | 保留 1 位小数的数字 |
8. 使用 float 和 double 时的常见错误
错误 №1:将 double 隐式转换为 float
float f = 1.23; // 错误!
编译器会不高兴:“你正尝试把 double 放进 float——这可能导致精度丢失!”务必添加后缀 f。
错误 №2:忘记了两个 int 相除得到的是 int 结果
int a = 7, b = 2;
double result = a / b; // 3.0,而不是 3.5
要得到小数部分,显式地把至少一个操作数转换为:
double result = (double) a / b; // 3.5
错误 №3:比较小数
不要使用 == 判断小数是否相等。应当使用带有很小容差(epsilon)的比较。
错误 №4:float 的精度丢失
不要在 float 中存放大数或需要高精度的值——它可能会“失真”或丢失关键数字。
GO TO FULL VERSION