CodeGym/Java 博客/随机的/Java 中的大数
John Squirrels
第 41 级
San Francisco

Java 中的大数

已在 随机的 群组中发布
个会员
你好!在今天的课程中,我们将讨论大数。不,我的意思是真的很大。我们以前多次遇到原始数据类型的值范围表。它看起来像这样:
原始类型 内存大小 取值范围
字节 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(不用于数组时) 对或错
最宽敞的整数数据类型是long。说到浮点数,那就是double但是,如果我们需要的数字大到连long都装不下怎么办?Long 数据类型具有相当大的可能值范围,但仍限于 64 位。如果我们的超大数需要 100 位,我们需要想出什么?幸运的是,我们不需要发明任何东西。对于这种情况,Java 有两个特殊类:BigInteger(用于整数)和BigDecimal(对于浮点数)。是什么让他们与众不同?首先,理论上,它们没有最大尺寸。我们说“理论上”,因为没有无限内存的计算机。如果您的程序创建的数字大于可用内存量,那么该程序当然将无法运行。但这种情况不太可能发生。因此,我们可以说BigIntegerBigDecimal可以表示几乎无限大小的数字。这些类有什么用?首先,对于精度要求极其严格的计算。例如,人的生命可能取决于某些程序(例如控制飞机、火箭或医疗设备的软件)中计算的准确性。所以如果第 150 位小数很重要,那么BigDecimal是最好的选择。此外,此类对象经常用于金融领域,即使是最小值的准确计算也极为重要。您如何使用BigIntegerBigDecimal对象,您是否需要了解它们?这些类的对象是这样创建的:
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);
   }
}
将字符串传递给构造函数只是一种可能的选择。这里我们使用字符串,因为我们的数字超过了longdouble的最大值,我们确实需要一些方法来向编译器解释我们要创建哪个数字 :) 只需传递数字 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 文档中找到完整的方法列表:此处此处)。
  1. 算术运算方法:add()subtract()multiply()divide()。这些方法分别用于执行加法、减法、乘法和除法。

  2. 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
  3. 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 种舍入模式:
  1. ROUND_CEILING — 向上舍入

    111.5555555555 -> setScale(3, ROUND_CEILING) -> 111.556
  2. ROUND_DOWN — 向零舍入

    111.5555555555 -> setScale(3, ROUND_DOWN) -> 111.555
  3. ROUND_FLOOR — 向下舍入

    111.5555555555 -> setScale(3, ROUND_FLOOR) -> 111.555

  4. ROUND_HALF_UP — 如果小数点后的数字 >= 0.5,则向上舍入

    0.55 -> setScale(1, ROUND_HALF_UP) -> 0.6
    0.54 -> setScale(1, ROUND_HALF_UP) -> 0.5
  5. ROUND_HALF_DOWN — 如果小数点后的数字 > 0.5,则向上舍入

    0.55 -> setScale(1, ROUND_HALF_DOWN) -> 0.5
    0.56 -> setScale(1, ROUND_HALF_DOWN) -> 0.6
  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。

  7. ROUND_UNNECCESSARY — 当您必须将舍入模式传递给方法但不需要舍入数字时使用此模式。如果您尝试使用 ROUND_UNNECCESSARY 模式集对数字进行舍入,则会抛出 ArithmeticException。

    3.51 -> setScale(1, ROUND_UNNECCESSARY) -> ArithmeticException
  8. ROUND_UP — 从零舍入。

    111.5551 -> setScale(3, ROUND_UP) -> 111.556

比较大的数字

这也很重要。你会记得我们使用equals()方法来比较 Java 中的对象。实现要么由语言本身提供(对于标准 Java 类),要么由程序员覆盖。但对于BigDecimal对象,不建议使用equals()方法进行比较。这是因为BigDecimal.equals()方法仅在 2 个数字具有相同的值和比例时才返回 true:让我们比较DoubleBigDecimal类的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 相等。而这正是我们所期望的结果!:) 今天的课程到此结束。现在是时候回到任务了!:)
评论
  • 受欢迎
你必须先登录才能发表评论
此页面还没有任何评论