杰瑞科技汇

Java double如何精确取小数?

在 Java 中,对 double 类型的小数部分进行操作,有几种常见的需求和方法,我将根据不同的需求场景,为你详细解释。

Java double如何精确取小数?-图1
(图片来源网络,侵删)

只取小数部分(保留小数点后的数字)

这是最常见的需求,456 取小数部分得到 456

方法1:使用取模运算符 () - 推荐

这是最直接、最简单的方法,一个数对 1 取模,得到的结果就是它的小数部分。

public class DoubleFractionalPart {
    public static void main(String[] args) {
        double number1 = 123.456;
        double number2 = -78.901;
        double number3 = 100.0; // 整数
        // 使用 % 运算符
        double fractionalPart1 = number1 % 1;
        double fractionalPart2 = number2 % 1;
        double fractionalPart3 = number3 % 1;
        System.out.println(number1 + " 的小数部分是: " + fractionalPart1); // 输出: 123.456 的小数部分是: 0.456
        System.out.println(number2 + " 的小数部分是: " + fractionalPart2); // 输出: -78.901 的小数部分是: -0.901
        System.out.println(number3 + " 的小数部分是: " + fractionalPart3); // 输出: 100.0 的小数部分是: 0.0
    }
}

注意:对于负数, 运算会保留负号,如果你希望小数部分永远是正数,可以取绝对值。

double number = -78.901;
double fractionalPart = Math.abs(number % 1); // 使用 Math.abs() 确保结果为正
System.out.println(fractionalPart); // 输出: 0.901

方法2:通过类型转换和减法

这种方法稍微复杂一些,但原理清晰。

Java double如何精确取小数?-图2
(图片来源网络,侵删)
  1. double 强制转换为 (int),这会直接截断小数部分,只保留整数部分。
  2. 用原数减去这个整数部分,得到小数部分。
public class DoubleFractionalPartMethod2 {
    public static void main(String[] args) {
        double number = 123.456;
        // 1. 强制转换为 int,截断小数部分
        int integerPart = (int) number; // integerPart 变为 123
        // 2. 原数减去整数部分
        double fractionalPart = number - integerPart;
        System.out.println(number + " 的小数部分是: " + fractionalPart); // 输出: 123.456 的小数部分是: 0.456
    }
}

注意:此方法对于负数同样需要注意符号问题。(int) -123.456 的结果是 -123number - integerPart 的结果是 -0.456


四舍五入到指定位数的小数

这个需求也非常普遍,比如将 456 四舍五入到两位小数,得到 46

方法1:使用 BigDecimal - 最精确(推荐用于金融计算)

BigDecimal 是处理高精度十进制数的首选,可以完美避免 doublefloat 的精度问题。

import java.math.BigDecimal;
import java.math.RoundingMode;
public class BigDecimalRounding {
    public static void main(String[] args) {
        double number = 123.456;
        int decimalPlaces = 2; // 保留两位小数
        // 1. 将 double 转换为 String 再构造 BigDecimal,避免直接 new BigDecimal(double) 的精度问题
        BigDecimal bd = new BigDecimal(String.valueOf(number));
        // 2. 使用 setScale 方法进行四舍五入
        BigDecimal rounded = bd.setScale(decimalPlaces, RoundingMode.HALF_UP);
        System.out.println("原始值: " + number);
        System.out.println("四舍五入后: " + rounded); // 输出: 四舍五入后: 123.46
    }
}

RoundingMode.HALF_UP 是最常用的四舍五入模式。BigDecimal 还提供了其他模式,如 UP (远离零舍入)、DOWN (向零舍入)、CEILING (向正无穷舍入) 等。

方法2:使用 Math.round() - 适用于整数位小数

Math.round() 会返回一个 longint,所以它适用于四舍五入到整数位,如果要保留小数位,需要一些技巧。

public class MathRoundRounding {
    public static void main(String[] args) {
        double number = 123.456;
        int decimalPlaces = 2;
        // 核心思路:先将小数点向右移动指定位数,四舍五入,再移回来
        // Math.pow(10, decimalPlaces) 得到 10的decimalPlaces次方
        double factor = Math.pow(10, decimalPlaces);
        // 1. 乘以因子
        double temp = number * factor; // 123.456 * 100 = 12345.6
        // 2. 四舍五入
        long roundedLong = Math.round(temp); // Math.round(12345.6) -> 12346
        // 3. 除以因子
        double result = (double) roundedLong / factor; // 12346.0 / 100 = 123.46
        System.out.println("原始值: " + number);
        System.out.println("四舍五入后: " + result); // 输出: 四舍五入后: 123.46
    }
}

注意Math.round() 本质上是 +0.5 然后向下取整,所以它存在 double 的精度问题,对于要求极高的场景,BigDecimal 更可靠。


截断(舍去)指定位数的小数

这个需求是直接砍掉指定位数之后的小数,不进行四舍五入。

方法1:使用 BigDecimal - 精确且简单

import java.math.BigDecimal;
import java.math.RoundingMode;
public class BigDecimalTruncating {
    public static void main(String[] args) {
        double number = 123.456;
        int decimalPlaces = 2;
        BigDecimal bd = new BigDecimal(String.valueOf(number));
        // 使用 RoundingMode.DOWN 来实现截断
        BigDecimal truncated = bd.setScale(decimalPlaces, RoundingMode.DOWN);
        System.out.println("原始值: " + number);
        System.out.println("截断后: " + truncated); // 输出: 截断后: 123.45
    }
}

方法2:使用类型转换和乘除法 - 不精确

public class TruncatingMethod2 {
    public static void main(String[] args) {
        double number = 123.456;
        int decimalPlaces = 2;
        double factor = Math.pow(10, decimalPlaces);
        // 1. 乘以因子
        double temp = number * factor; // 123.456 * 100 = 12345.6
        // 2. 强制转换为 long (会截断小数部分)
        long truncatedLong = (long) temp; // (long) 12345.6 -> 12345
        // 3. 除以因子
        double result = (double) truncatedLong / factor; // 12345.0 / 100 = 123.45
        System.out.println("原始值: " + number);
        System.out.println("截断后: " + result); // 输出: 截断后: 123.45
    }
}

注意:此方法同样受 double 精度问题的影响,不推荐用于需要精确计算的场景。


总结与建议

需求 推荐方法 优点 缺点
只取小数部分 number % 1 简单、直接、性能高 负数结果为负,需用 Math.abs() 处理
四舍五入到N位 BigDecimal.setScale() 精度极高,功能强大(多种舍入模式) 代码稍长,需要引入 java.math
截断到N位 BigDecimal.setScale(..., RoundingMode.DOWN) 精度极高,代码清晰 需要引入 java.math

核心建议:

  • 如果只是简单地获取小数部分,使用 运算符。
  • 如果涉及到金融、货币或任何对精度要求苛刻的场景,请务必使用 BigDecimal
  • 避免使用 floatdouble 来表示精确的十进制数值,因为它们是基于二进制的,无法精确表示某些十进制小数(如 0.1)。
分享:
扫描分享到社交APP
上一篇
下一篇