你好!在今天的课程中,我们将讨论大数。不,我的意思是真的很大。我们以前多次遇到原始数据类型的值范围表。它看起来像这样:
最宽敞的整数数据类型是long。说到浮点数,那就是double。但是,如果我们需要的数字大到连long都装不下怎么办?Long 数据类型具有相当大的可能值范围,但仍限于 64 位。如果我们的超大数需要 100 位,我们需要想出什么?幸运的是,我们不需要发明任何东西。对于这种情况,Java 有两个特殊类:BigInteger(用于整数)和BigDecimal(对于浮点数)。是什么让他们与众不同?首先,理论上,它们没有最大尺寸。我们说“理论上”,因为没有无限内存的计算机。如果您的程序创建的数字大于可用内存量,那么该程序当然将无法运行。但这种情况不太可能发生。因此,我们可以说BigInteger和BigDecimal可以表示几乎无限大小的数字。这些类有什么用?首先,对于精度要求极其严格的计算。例如,人的生命可能取决于某些程序(例如控制飞机、火箭或医疗设备的软件)中计算的准确性。所以如果第 150 位小数很重要,那么BigDecimal是最好的选择。此外,此类对象经常用于金融领域,即使是最小值的准确计算也极为重要。您如何使用BigInteger和BigDecimal对象,您是否需要了解它们?这些类的对象是这样创建的:
原始类型 | 内存大小 | 取值范围 |
---|---|---|
字节 | 8位 | -128 到 127 |
短的 | 16位 | -32768 至 32767 |
字符 | 16位 | 0 到 65536 |
整数 | 32位 | -2147483648 至 2147483647 |
长的 | 64位 | -9223372036854775808 至 9223372036854775807 |
漂浮 | 32位 | (2 的 -149 次方) 到 ((2 的 -23 次方) * 2 的 127 次方) |
双倍的 | 64位 | (-2 的 63 次方) 到 ((2 的 63 次方) - 1) |
布尔值 | 8(用于数组时),32(不用于数组时) | 对或错 |
public class Main {
public static void main(String[] args) {
BigInteger integer = new BigInteger("11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111");
System.out.println(integer);
BigDecimal decimal = new BigDecimal("123.444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444");
System.out.println(decimal);
}
}
将字符串传递给构造函数只是一种可能的选择。这里我们使用字符串,因为我们的数字超过了long和double的最大值,我们确实需要一些方法来向编译器解释我们要创建哪个数字 :) 只需传递数字 111111111111111111111111111111111111111111111111111111111111111111111 1111111111111111111111111111111111111111111111111111111111111111111 到构造函数将不起作用:Java 将尝试将传递的数字填充到一种原始数据类型中,但它不适合其中任何一种。这就是为什么使用字符串传递所需数字是一个不错的选择。这两个类都可以自动从传递的字符串中提取数值。使用大数类时要记住的另一个要点是它们的对象是不可变的 ( Immutable )。由于您使用String类和基本类型(Integer、Long 等)的包装类的经验,您已经熟悉了不可变性。
import java.math.BigInteger;
public class Main {
public static void main(String[] args) {
BigInteger integer = new BigInteger("11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111");
System.out.println(integer);
integer.add(BigInteger.valueOf(33333333));
System.out.println(integer);
}
}
控制台输出:
11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
如您所料,我们的号码没有改变。要执行加法运算,您必须创建一个新对象来接收运算结果。
import java.math.BigInteger;
public class Main {
public static void main(String[] args) {
BigInteger integer = new BigInteger("11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111");
System.out.println(integer);
BigInteger result = integer.add(BigInteger.valueOf(33333333));
System.out.println(result);
}
}
控制台输出:
11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111144444444
看,现在一切正常了 :) 顺便说一下,您是否注意到加法运算看起来有多么不寻常?
BigInteger result = integer.add(BigInteger.valueOf(33333333));
这是另一个重点。大数类不使用 + - * / 运算符。相反,它们提供了一组方法。让我们熟悉一下主要方法(一如既往,您可以在 Oracle 文档中找到完整的方法列表:此处和此处)。
-
算术运算方法:add()、subtract()、multiply()、divide()。这些方法分别用于执行加法、减法、乘法和除法。
-
doubleValue()、intValue()、floatValue()、longValue()等用于将大数转换为 Java 的原始类型之一。使用这些方法时要小心。不要忘记位大小的差异!
import java.math.BigInteger; public class Main { public static void main(String[] args) { BigInteger integer = new BigInteger("11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"); long result = integer.longValue(); System.out.println(result); } }
控制台输出:
8198552921648689607
-
min()和max()让你找到两个大数的最小值和最大值。
请注意,这些方法不是静态的!import java.math.BigInteger; public class Main { public static void main(String[] args) { BigInteger integer = new BigInteger("11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"); BigInteger integer2 = new BigInteger("222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222"); System.out.println(integer.max(integer2)); } }
控制台输出:
222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222
BigDecimal 舍入行为
本主题有其单独的部分,因为对大数字进行舍入和配置舍入行为并不是那么简单。您可以使用setScale()方法设置BigDecimal的小数位数。例如,假设我们希望数字111.5555555555 的小数点后保留三位数字。但是,我们不能通过将数字 3 作为参数传递给setScale()方法来实现我们想要的。如上所述,BigDecimal用于表示对计算精度有严格要求的数字。在当前形式中,我们的数字在小数点后有 10 位数字。我们要去掉其中的 7 个,只保留 3 个。因此,除了数字 3 之外,我们还必须通过舍入模式。 BigDecimal共有 8 种舍入模式。好多啊!但是,如果您真的需要微调计算的精度,您将拥有所需的一切。因此,这是BigDecimal提供的 8 种舍入模式:-
ROUND_CEILING — 向上舍入
111.5555555555 -> setScale(3, ROUND_CEILING) -> 111.556
-
ROUND_DOWN — 向零舍入
111.5555555555 -> setScale(3, ROUND_DOWN) -> 111.555
-
ROUND_FLOOR — 向下舍入
111.5555555555 -> setScale(3, ROUND_FLOOR) -> 111.555
-
ROUND_HALF_UP — 如果小数点后的数字 >= 0.5,则向上舍入
0.55 -> setScale(1, ROUND_HALF_UP) -> 0.6 0.54 -> setScale(1, ROUND_HALF_UP) -> 0.5
-
ROUND_HALF_DOWN — 如果小数点后的数字 > 0.5,则向上舍入
0.55 -> setScale(1, ROUND_HALF_DOWN) -> 0.5 0.56 -> setScale(1, ROUND_HALF_DOWN) -> 0.6
-
ROUND_HALF_EVEN — 四舍五入取决于小数点左边的数字。如果左边的数字是偶数,则向下舍入。如果小数点左边的数是奇数,则向上舍入。
2.5 -> setScale(0, ROUND_HALF_EVEN) -> 2
小数点左边的数字是 2(偶数)。该数字向下舍入。我们想要 0 位小数,所以结果是 2。
3.5 -> setScale(0, ROUND_HALF_EVEN) -> 4
小数点左边的数字是 3(奇数)。数字四舍五入。我们想要 0 位小数,所以结果是 4。
-
ROUND_UNNECCESSARY — 当您必须将舍入模式传递给方法但不需要舍入数字时使用此模式。如果您尝试使用 ROUND_UNNECCESSARY 模式集对数字进行舍入,则会抛出 ArithmeticException。
3.51 -> setScale(1, ROUND_UNNECCESSARY) -> ArithmeticException
-
ROUND_UP — 从零舍入。
111.5551 -> setScale(3, ROUND_UP) -> 111.556
比较大的数字
这也很重要。你会记得我们使用equals()方法来比较 Java 中的对象。实现要么由语言本身提供(对于标准 Java 类),要么由程序员覆盖。但对于BigDecimal对象,不建议使用equals()方法进行比较。这是因为BigDecimal.equals()方法仅在 2 个数字具有相同的值和比例时才返回 true:让我们比较Double和BigDecimal类的equals()方法的行为:
import java.math.BigDecimal;
public class Main {
public static void main(String[] args) {
Double a = 1.5;
Double b = 1.50;
System.out.println(a.equals(b));
BigDecimal x = new BigDecimal("1.5");
BigDecimal y = new BigDecimal("1.50");
System.out.println(x.equals(y));
}
}
控制台输出:
true
false
如您所见,对于BigDecimal,数字 1.5 和 1.50 结果是不相等的!这正是因为BigDecimal类中equals()方法的实现细节。为了更准确地比较两个BigDecimal对象,最好使用compareTo()方法:
import java.math.BigDecimal;
public class Main {
public static void main(String[] args) {
BigDecimal x = new BigDecimal("1.5");
BigDecimal y = new BigDecimal("1.50");
System.out.println(x.compareTo(y));
}
}
控制台输出:
0
compareTo ()方法返回 0,这意味着 1.5 和 1.50 相等。而这正是我们所期望的结果!:) 今天的课程到此结束。现在是时候回到任务了!:)
GO TO FULL VERSION