杰瑞科技汇

Java double如何保留一位小数?

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

Java double如何保留一位小数?-图1
(图片来源网络,侵删)

核心概念: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 的格式化操作,其核心思想是:在显示(或转换为字符串)的时候进行格式化,而不是修改其底层的二进制值

Java double如何保留一位小数?-图2
(图片来源网络,侵删)

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)、添加千位分隔符等。

缺点:

Java double如何保留一位小数?-图3
(图片来源网络,侵删)
  • 返回的是 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 类型(当参数是 floatdouble 时),你需要先将其除以 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 最高,精确计算 金融、货币、任何对精度要求苛刻的场景 ⭐⭐⭐⭐ (特定场景)

如何选择?

  1. 如果你的目的是显示或打印:比如在控制台输出、显示在网页上或写入日志文件。

    • 首选 String.format(),因为它最简单直接。
    • 如果需要在多处重复使用同一种格式,可以考虑 DecimalFormat
  2. 如果你的目的是为了后续的数学计算:比如将四舍五入后的结果继续参与运算。

    • 优先考虑 BigDecimal,因为它能保证计算的精确性,这是专业做法。
    • 如果性能是首要考虑因素,且对精度要求不那么苛刻(例如游戏物理引擎),可以使用 Math.round(),但要清楚其潜在风险。
  3. 如果你正在处理金融或货币数据

    • 必须使用 BigDecimal,永远不要用 doublefloat 来直接计算金额。
分享:
扫描分享到社交APP
上一篇
下一篇