CodeGym/Blog Java/Ngẫu nhiên/Số thập phân lớn trong Java

Số thập phân lớn trong Java

Xuất bản trong nhóm
CHÀO! Trong bài học hôm nay, chúng ta sẽ nói về số lớn. Không, ý tôi là RẤT LỚN. Trước đây chúng ta đã nhiều lần bắt gặp bảng phạm vi giá trị cho các kiểu dữ liệu nguyên thủy. Nó trông như thế này:
loại nguyên thủy Kích thước trong bộ nhớ Phạm vi giá trị
byte 8 bit -128 đến 127
ngắn 16 bit -32768 đến 32767
than 16 bit 0 đến 65536
int 32 bit -2147483648 đến 2147483647
dài 64bit -9223372036854775808 đến 9223372036854775807
trôi nổi 32 bit (2 mũ -149) thành ((2 mũ -23) * 2 mũ 127)
gấp đôi 64bit (-2 mũ 63) đến ((2 mũ 63) - 1)
boolean 8 (khi được sử dụng trong mảng), 32 (khi không được sử dụng trong mảng) đúng hay sai
Kiểu dữ liệu số nguyên rộng rãi nhất là long . Khi nói đến số dấu phẩy động, đó là số double . Nhưng điều gì sẽ xảy ra nếu số lượng chúng ta cần quá lớn đến mức nó thậm chí không vừa với long ? Kiểu dữ liệu Long có phạm vi giá trị có thể khá lớn, nhưng nó vẫn bị giới hạn ở 64 bit. Chúng ta cần nghĩ ra điều gì nếu Số rất lớn của chúng ta yêu cầu 100 bit? May mắn thay, chúng ta không cần phải phát minh ra bất cứ thứ gì. Đối với những trường hợp như thế này, Java có hai lớp đặc biệt: BigInteger (dành cho số nguyên) và BigDecimal(đối với số dấu phẩy động). Điều gì khiến chúng trở nên đặc biệt? Trước hết, về lý thuyết, chúng không có kích thước tối đa. Chúng tôi nói "về lý thuyết", bởi vì không có máy tính nào có bộ nhớ vô hạn. Và nếu chương trình của bạn tạo ra một số lớn hơn dung lượng bộ nhớ khả dụng, thì tất nhiên, chương trình sẽ không hoạt động. Nhưng những trường hợp như vậy là không thể. Kết quả là, chúng ta có thể nói rằng BigIntegerBigDecimal có thể biểu diễn các số có kích thước hầu như không giới hạn. Những lớp này được sử dụng để làm gì? Trước hết, đối với các phép tính có yêu cầu cực kỳ khắt khe về độ chính xác. Ví dụ, tính mạng con người có thể phụ thuộc vào độ chính xác của các tính toán trong một số chương trình (ví dụ: phần mềm điều khiển máy bay, tên lửa hoặc thiết bị y tế). Vì vậy, nếu vị trí thập phân thứ 150 là quan trọng, thì BigDecimallà sự lựa chọn tốt nhất. Ngoài ra, các đối tượng thuộc lớp này thường được sử dụng trong thế giới tài chính, nơi mà việc tính toán chính xác ngay cả những giá trị nhỏ nhất cũng cực kỳ quan trọng. Bạn làm việc với các đối tượng BigIntegerBigDecimal như thế nào và bạn có cần biết về chúng không? Các đối tượng của các lớp này được tạo ra như thế này:
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);
   }
}
Truyền một chuỗi cho hàm tạo chỉ là một tùy chọn khả thi. Ở đây chúng tôi sử dụng chuỗi, bởi vì số của chúng tôi vượt quá giá trị tối đa cho longdouble và chúng tôi cần một số cách để giải thích cho trình biên dịch số mà chúng tôi muốn tạo :) Chỉ cần chuyển số 11111111111111111111111111111111111111111111111111111111111111111111111111111 1111111111111111111111111111111111111111111111111111111111111 đến hàm tạo sẽ không hoạt động: Java sẽ cố gắng nhồi nhét số đã truyền vào một trong các kiểu dữ liệu nguyên thủy, nhưng nó sẽ không phù hợp với bất kỳ kiểu nào trong số đó. Đó là lý do tại sao sử dụng một chuỗi để chuyển số mong muốn là một lựa chọn tốt. Cả hai lớp có thể tự động trích xuất các giá trị số từ các chuỗi đã truyền. Một điểm quan trọng khác cần nhớ khi làm việc với các lớp số lớn là các đối tượng của chúng là bất biến ( Immutable ). Bạn đã quen thuộc với tính bất biến nhờ kinh nghiệm của bạn với lớp Chuỗi và các lớp bao bọc cho các kiểu nguyên thủy (Số nguyên, Dài, v.v.).
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);

   }
}
Đầu ra bảng điều khiển:
11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
Như bạn mong đợi, số của chúng tôi đã không thay đổi. Để thực hiện phép cộng, bạn phải tạo một đối tượng mới để nhận kết quả của phép toán.
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);

   }
}
Đầu ra bảng điều khiển:
11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111144444444
Thấy chưa, bây giờ mọi thứ hoạt động bình thường :) Nhân tiện, bạn có nhận thấy thao tác cộng trông khác thường như thế nào không?
BigInteger result = integer.add(BigInteger.valueOf(33333333));
Đây là một điểm quan trọng khác. Các lớp số lớn không sử dụng toán tử + - * /. Thay vào đó, họ cung cấp một tập hợp các phương pháp. Hãy làm quen với những cái chính (như mọi khi, bạn có thể tìm thấy danh sách đầy đủ các phương thức trong tài liệu Oracle: tại đâytại đây ).
  1. các phương thức cho các phép toán số học: add() , trừ() , nhân() , chia() . Các phương thức này được sử dụng để thực hiện phép cộng, phép trừ, phép nhân và phép chia tương ứng.

  2. doubleValue() , intValue() , floatValue() , longValue() , v.v. được sử dụng để chuyển đổi một số lớn thành một trong các kiểu nguyên thủy của Java. Hãy cẩn thận khi sử dụng các phương pháp này. Đừng quên sự khác biệt về kích thước bit!

    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);
    
       }
    }

    Đầu ra bảng điều khiển:

    8198552921648689607
  3. min()max() cho phép bạn tìm giá trị nhỏ nhất và lớn nhất của hai số lớn.
    Lưu ý rằng các phương thức này không tĩnh!

    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));
    
       }
    }

    Đầu ra bảng điều khiển:

    222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222

Hành vi làm tròn BigDecimal

Chủ đề này có phần riêng, vì làm tròn số lớn và định cấu hình hành vi làm tròn không đơn giản như vậy. Bạn có thể sử dụng phương thức setScale() để đặt số vị trí thập phân cho BigDecimal . Ví dụ: giả sử chúng ta muốn số 111.5555555555 có ba chữ số sau dấu thập phân. Tuy nhiên, chúng ta không thể đạt được điều mình muốn bằng cách chuyển số 3 làm đối số cho phương thức setScale() . Như đã đề cập ở trên, BigDecimallà để biểu diễn các số có yêu cầu nghiêm ngặt về độ chính xác tính toán. Ở dạng hiện tại, số của chúng tôi có 10 chữ số sau dấu thập phân. Chúng tôi muốn bỏ 7 trong số đó và chỉ giữ lại 3. Theo đó, ngoài số 3, chúng tôi phải vượt qua chế độ làm tròn. BigDecimal có tổng cộng 8 chế độ làm tròn. Đó là rất nhiều! Nhưng nếu bạn thực sự cần tinh chỉnh độ chính xác của phép tính, bạn sẽ có mọi thứ mình cần. Vì vậy, đây là 8 chế độ làm tròn được cung cấp bởi BigDecimal :
  1. ROUND_CEILING — làm tròn lên

    111.5555555555 -> setScale(3, ROUND_CEILING) -> 111.556
  2. ROUND_DOWN — làm tròn về 0

    111.5555555555 -> setScale(3, ROUND_DOWN) -> 111.555
  3. ROUND_FLOOR — làm tròn xuống

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

  4. ROUND_HALF_UP — làm tròn lên nếu số sau dấu thập phân >= 0,5

    0.55 -> setScale(1, ROUND_HALF_UP) -> 0.6
    0.54 -> setScale(1, ROUND_HALF_UP) -> 0.5
  5. ROUND_HALF_DOWN — làm tròn lên nếu số sau dấu thập phân > 0,5

    0.55 -> setScale(1, ROUND_HALF_DOWN) -> 0.5
    0.56 -> setScale(1, ROUND_HALF_DOWN) -> 0.6
  6. ROUND_HALF_EVEN — làm tròn phụ thuộc vào số ở bên trái dấu thập phân. Nếu số bên trái là số chẵn thì làm tròn xuống. Nếu số ở bên trái dấu thập phân là số lẻ thì sẽ làm tròn lên.

    2.5 -> setScale(0, ROUND_HALF_EVEN) -> 2

    Số ở bên trái chữ số thập phân là 2 (chẵn). Số được làm tròn xuống. Chúng tôi muốn 0 chữ số thập phân, vì vậy kết quả là 2.

    3.5 -> setScale(0, ROUND_HALF_EVEN) -> 4

    Số ở bên trái dấu thập phân là 3 (lẻ). Số được làm tròn lên. Chúng tôi muốn 0 chữ số thập phân, vì vậy kết quả là 4.

  7. ROUND_UNNECCESSARY — Chế độ này được sử dụng khi bạn phải chuyển chế độ làm tròn cho một phương thức nhưng số không cần phải làm tròn. Nếu bạn cố gắng làm tròn một số với chế độ ROUND_UNNECCESSARY được đặt, một ArithmeticException sẽ được đưa ra.

    3.51 -> setScale(1, ROUND_UNNECCESSARY) -> ArithmeticException
  8. ROUND_UP — làm tròn từ số không.

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

So sánh số lớn

Điều này cũng quan trọng. Bạn sẽ nhớ lại rằng chúng ta sử dụng phương thức equals() để so sánh các đối tượng trong Java. Việc triển khai được cung cấp bởi chính ngôn ngữ (đối với các lớp Java tiêu chuẩn) hoặc được lập trình viên ghi đè. Nhưng trong trường hợp đối tượng BigDecimal , không nên sử dụng phương thức equals() để so sánh. Điều này là do phương thức BigDecimal.equals() chỉ trả về true nếu 2 số có cùng giá trị và tỷ lệ: Hãy so sánh hành vi của phương thức equals() cho các lớp DoubleBigDecimal :
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));

   }
}
Đầu ra bảng điều khiển:
true
false
Như bạn có thể thấy, đối với BigDecimal , các số 1,5 và 1,50 hóa ra không bằng nhau! Điều này chính xác là do các chi tiết cụ thể của việc triển khai phương thức equals() trong lớp BigDecimal . Để so sánh chính xác hơn hai đối tượng BigDecimal , tốt hơn là sử dụng phương thức 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));

   }
}
Đầu ra bảng điều khiển:
0
Phương thức so sánh() trả về 0, có nghĩa là 1,5 và 1,50 bằng nhau. Và đây là kết quả chúng ta mong đợi! :) Điều đó kết thúc bài học của chúng tôi ngày hôm nay. Bây giờ là lúc để trở lại với các nhiệm vụ! :)
Bình luận
  • Phổ biến
  • Mới
Bạn phải đăng nhập để đăng nhận xet
Trang này chưa có bất kỳ bình luận nào