在 Java 中,要将一个 double 类型的小数保留一位小数,有几种常见的方法,我会从最简单、最常用的方法开始,并逐步介绍其他方法,同时指出它们的优缺点和适用场景。

核心概念:double 的精度问题
要明白一个非常重要的概念:double 类型本身无法精确存储所有小数,它是一种基于二进制的浮点数,在计算机内存中存储的是近似值。
1 在二进制中是一个无限循环小数,double 类型的 1 实际上存储的是一个非常接近但不完全等于 1 的值。
double d = 0.1; System.out.println(d); // 输出可能是 0.1,但它的实际值是 0.1000000000000000055511151231257827021181583404541015625
这意味着,当你尝试对 double 进行四舍五入时,由于这个微小的精度误差,可能会得到意想不到的结果。
double d = 123.1256; // 错误的期望:直接四舍五入到 123.1 // d 的值可能比 123.1256 稍微小一点 System.out.println(Math.floor(d * 10) / 10); // 可能输出 123.1,但不可靠
所有对 double 的格式化操作,其核心思想是:在显示(或转换为字符串)的时候进行格式化,而不是修改其底层的二进制值。

String.format() 或 System.out.printf() (最常用)
这是最推荐的方法,因为它简单、直观,并且专门用于格式化输出,它不会改变原始 double 变量的值,只是在生成字符串时进行格式化。
语法
%.1f 表示格式化为一个浮点数,并保留 1 位小数。
示例
double price = 123.456;
double anotherPrice = 78.9;
// 使用 String.format() 获取格式化后的字符串
String formattedPrice1 = String.format("%.1f", price);
String formattedPrice2 = String.format("%.1f", anotherPrice);
System.out.println("原始值: " + price); // 输出: 原始值: 123.456
System.out.println("格式化后: " + formattedPrice1); // 输出: 格式化后: 123.5
System.out.println("原始值: " + anotherPrice); // 输出: 原始值: 78.9
System.out.println("格式化后: " + formattedPrice2); // 输出: 格式化后: 78.9
优点:
- 简单易用:语法清晰明了。
- 不改变原值:只生成新的字符串,不影响原始的
double变量。 - 功能强大:可以轻松扩展为保留更多位小数(如
%.2f)、添加千位分隔符等。
缺点:

- 返回的是
String类型,如果后续还需要进行数学计算,你需要先将其转换回double,这可能会再次引入精度问题。
DecimalFormat 类 (更灵活)
java.text.DecimalFormat 是一个专门用于格式化数字的类,功能比 String.format() 更强大,尤其适合需要重复使用某种固定格式模式的场景。
示例
import java.text.DecimalFormat;
double number = 9876.54321;
// 1. 创建 DecimalFormat 对象,并指定格式模式
DecimalFormat df = new DecimalFormat("#.#");
// 2. 使用 format() 方法进行格式化
String formattedNumber = df.format(number);
System.out.println("原始值: " + number); // 输出: 原始值: 9876.54321
System.out.println("格式化后: " + formattedNumber); // 输出: 格式化后: 9876.5
模式说明:
- 表示一个数字,如果该位没有数字则不显示。
0:表示一个数字,如果该位没有数字则显示为0。- 模式
00格式化6会得到60。
- 模式
优点:
- 可重用性高:可以将
DecimalFormat实例化并重复使用。 - 模式丰富:支持更复杂的格式化模式,如货币、百分比等。
缺点:
- 需要导入
java.text.DecimalFormat包。 - 同样,返回的是
String类型。
Math.round() (四舍五入为整数,再转换)
如果你想将结果四舍五入后仍然是一个 double 类型,可以使用 Math.round(),但它的用法需要一些技巧。
注意: Math.round() 的返回值是 long 类型(当参数是 float 或 double 时),你需要先将其除以 0 来得到一位小数。
示例
double value = 123.56;
// 1. 乘以 10
// 2. 四舍五入
// 3. 除以 10.0 (注意用 10.0,否则结果会是整数)
double roundedValue = Math.round(value * 10) / 10.0;
System.out.println("原始值: " + value); // 输出: 原始值: 123.56
System.out.println("四舍五入后: " + roundedValue); // 输出: 四舍五入后: 123.6
优点:
- 返回的是
double类型,可以直接用于后续的数学计算。
缺点:
- 存在精度风险:由于
double的精度问题,value * 10的结果可能不精确,导致Math.round()的结果出错。125可能会因为精度问题被Math.round(1231.25)处理,最终得到1而不是期望的2。 - 语法稍显复杂:需要理解“乘-舍-除”的逻辑。
BigDecimal 类 (最精确,用于金融计算)
对于金融、货币等对精度要求极高的场景,应该使用 java.math.BigDecimal 类,它可以精确地表示十进制小数,避免了 double 的精度问题。
示例
import java.math.BigDecimal;
import java.math.RoundingMode;
// 最好用字符串来构造 BigDecimal,以避免 double 的精度问题
double rawValue = 123.456;
BigDecimal bd = new BigDecimal(rawValue); // 注意:这里仍然有精度风险!
// 推荐方式:BigDecimal bd = new BigDecimal("123.456");
// 设置保留 1 位小数,并使用四舍五入模式
BigDecimal roundedBD = bd.setScale(1, RoundingMode.HALF_UP);
// 将 BigDecimal 转换回 double
double finalValue = roundedBD.doubleValue();
System.out.println("原始值: " + rawValue); // 输出: 原始值: 123.456
System.out.println("BigDecimal 格式化后: " + roundedBD); // 输出: BigDecimal 格式化后: 123.5
System.out.println("double 值: " + finalValue); // 输出: double 值: 123.5
优点:
- 精度最高:完全避免了二进制浮点数带来的精度问题,是处理金额和商业计算的黄金标准。
缺点:
- 使用最复杂:需要导入包,构造方式有讲究(推荐用字符串),API 也相对复杂。
- 性能开销比
double大。
总结与推荐
| 方法 | 返回类型 | 精度 | 主要用途 | 推荐度 |
|---|---|---|---|---|
String.format() |
String |
显示时格式化,不影响原值 | 通用场景,特别是需要将结果展示给用户(如日志、UI界面) | ⭐⭐⭐⭐⭐ (首选) |
DecimalFormat |
String |
显示时格式化,不影响原值 | 需要重复使用固定格式模式的场景 | ⭐⭐⭐⭐ |
Math.round() |
double |
有风险,受 double 精度影响 |
需要对结果进行后续数学计算,且对精度要求不高 | ⭐⭐ |
BigDecimal |
BigDecimal |
最高,精确计算 | 金融、货币、任何对精度要求苛刻的场景 | ⭐⭐⭐⭐ (特定场景) |
如何选择?
-
如果你的目的是显示或打印:比如在控制台输出、显示在网页上或写入日志文件。
- 首选
String.format(),因为它最简单直接。 - 如果需要在多处重复使用同一种格式,可以考虑
DecimalFormat。
- 首选
-
如果你的目的是为了后续的数学计算:比如将四舍五入后的结果继续参与运算。
- 优先考虑
BigDecimal,因为它能保证计算的精确性,这是专业做法。 - 如果性能是首要考虑因素,且对精度要求不那么苛刻(例如游戏物理引擎),可以使用
Math.round(),但要清楚其潜在风险。
- 优先考虑
-
如果你正在处理金融或货币数据:
- 必须使用
BigDecimal,永远不要用double或float来直接计算金额。
- 必须使用
