Java BigDecimal
上次修改时间:2024 年 7 月 4 日
在本文中,我们将展示如何在 Java 中使用 BigDecimal 执行高精度计算。
BigDecimal
表示一个不可变的、任意精度的有符号十进制数。它用于高精度算术。BigDecimal
提供了算术、标度操作、舍入、比较、哈希和格式转换的操作。
BigDecimal
由两部分组成
- 未缩放值 - 一个任意精度的整数
- 标度 - 一个 32 位整数,表示小数点右边的位数
例如,BigDecimal
2.18 的未缩放值为 218,标度为 2。
BigDecimal
用于需要高精度的领域;例如,财务或货币计算。 BigDecimal
值的算术运算通过诸如 add
或 subtract
之类的方法完成; +
、-
、*
、\
运算符未重载。
BigDecimal 基本算术运算
以下示例显示了使用 BigDecimal
的基本算术运算。
import java.math.BigDecimal; import java.math.RoundingMode; void main() { var val1 = new BigDecimal("3.44"); var val2 = new BigDecimal("2.74"); BigDecimal res1 = val1.add(val2); System.out.println(res1); BigDecimal res2 = val1.subtract(val2); System.out.println(res2); BigDecimal res3 = val1.multiply(val2); System.out.println(res3); BigDecimal res4 = val1.divide(BigDecimal.TEN, RoundingMode.DOWN); System.out.println(res4); BigDecimal res5 = val1.divide(val2, 15, RoundingMode.HALF_UP); System.out.println(res5); }
在此示例中,我们有加法、减法、乘法和除法运算。
var val1 = new BigDecimal("3.44"); var val2 = new BigDecimal("2.74");
我们创建两个 BigDecimal
值。 这些数字作为字符串传递。
BigDecimal res1 = val1.add(val2); System.out.println(res1);
在这里,我们将两个 BigDecimal
数字相加。 加法运算通过 add
方法执行。
BigDecimal res4 = val1.divide(BigDecimal.TEN, RoundingMode.DOWN); System.out.println(res4);
在进行除法运算时,我们还需要指定舍入模式。
$ java Main.java 6.18 0.70 9.4256 0.34 1.255474452554745
BigDecimal 精度
BigDecimal
用于高精度算术。
import java.math.BigDecimal; void main() { double a = 0.1 + 0.1 + 0.1; double b = 0.3; System.out.println(a); System.out.println(b); System.out.println(a == b); var c = new BigDecimal("0.1").add(new BigDecimal("0.1")) .add(new BigDecimal("0.1")); var d = new BigDecimal("0.3"); System.out.println(c); System.out.println(d); System.out.println(c.equals(d)); }
在此示例中,我们将 double
类型的精度与 BigDecimal
进行比较。 我们添加三个浮点值,并将其与预期输出进行比较。
$ java Main.java 0.30000000000000004 0.3 false 0.3 0.3 true
double
存在很小的边际误差; 因此,该操作不精确。 BigDecimal
给出预期的输出。
BigDecimal 舍入模式
BigDecimal
类允许其用户完全控制舍入行为。 如果未指定舍入模式并且无法表示确切的结果,则会抛出异常。
import java.math.BigDecimal; import java.math.RoundingMode; void main() { var x = new BigDecimal("5.54"); BigDecimal x2 = x.setScale(1, RoundingMode.FLOOR); System.out.println(x2); var y = new BigDecimal("5.94"); BigDecimal y2 = y.setScale(1, RoundingMode.CEILING); System.out.println(y2); }
在此示例中,我们在两种不同的舍入模式下舍入两个值。
var x = new BigDecimal("5.54"); BigDecimal x2 = x.setScale(1, RoundingMode.FLOOR);
使用 setScale
方法,我们提供标度和舍入模式。 在我们的例子中,我们将值四舍五入到一位小数,并使用舍入模式 RoundingMode.FLOOR
,它向负无穷大舍入。
$ java Main.java 5.5 6.0
Java BigDecimal 比较
使用 compareTo
方法,值相等但标度不同的两个 BigDecimal
对象(如 4.0 和 4.00)被此方法视为相等。
import java.math.BigDecimal; void main() { var x = new BigDecimal("1.6"); var y = new BigDecimal("1.60"); System.out.println(x.equals(y)); System.out.println(x.compareTo(y)); }
该示例使用 equals
和 compareTo
比较值 1.6 和 1.60。
$ java Main.java false 0
对于相等的值,compareTo
返回 0。
BigDecimal 实际示例
以下示例按类别对产品进行分组,并计算每个类别中所有产品的总价。
import java.math.BigDecimal; import java.util.List; import java.util.Map; import java.util.stream.Collectors; record Product(String name, String category, BigDecimal price) {} void main() { Map<String, Map<BigDecimal, List<Product>>> productsByCategories = products().stream().collect( Collectors.groupingBy(Product::category, Collectors.groupingBy(Product::price))); productsByCategories.forEach((k, v) -> { System.out.printf("%s: ", k); var sum = new BigDecimal("0"); var prices = v.keySet(); for (var price: prices) { sum = sum.add(price); } System.out.println(sum); }); } List<Product> products() { return List.of( new Product("apple", "fruit", new BigDecimal("4.50")), new Product("banana", "fruit", new BigDecimal("3.76")), new Product("carrot", "vegetables", new BigDecimal("2.98")), new Product("potato", "vegetables", new BigDecimal("0.92")), new Product("garlic", "vegetables", new BigDecimal("1.32")), new Product("ginger", "vegetables", new BigDecimal("2.45")), new Product("white bread", "bakery", new BigDecimal("1.50")), new Product("roll", "bakery", new BigDecimal("0.08")), new Product("bagel", "bakery", new BigDecimal("0.15")) ); }
我们有三种类别的各种杂货:水果、蔬菜和烘焙食品。 我们的目标是按类别对产品进行分组,并计算各个类别中所有价格的总和。
Map<String, Map<BigDecimal, List<Product>>> productsByCategories = products().stream().collect( Collectors.groupingBy(Product::category, Collectors.groupingBy(Product::price)));
使用 Java 流,我们按类别和价格对产品进行分组。
productsByCategories.forEach((k, v) -> { System.out.printf("%s: ", k); var sum = new BigDecimal("0"); var prices = v.keySet(); for (var price: prices) { sum = sum.add(price); } System.out.println(sum); });
在 forEach
循环中,我们计算每个类别中所有价格的总和。
$ java Main.java bakery: 1.73 fruit: 8.26 vegetables: 7.67
来源
在本文中,我们展示了如何在 Java 中使用 BigDecimal
进行高精度算术运算。
作者
列出所有Java教程。