Giải thích double trong Java: Phạm vi, Độ chính xác, Những bẫy thường gặp và Các giải pháp thay thế bằng BigDecimal

目次

1. Kiểu double của Java là gì?

Kiểu double của Java là một kiểu dữ liệu cơ bản để làm việc với các giá trị thập phân. Không giống như int hay long, những kiểu này đại diện cho số nguyên, double được dùng để biểu diễn các số có dấu chấm thập phân như “1.5”, “3.14” hoặc “0.01”. Đây là một trong những kiểu được sử dụng thường xuyên nhất khi thực hiện các phép tính số học trong Java.

Vì người mới bắt đầu thường gặp khó khăn ở đây, nên quan trọng là phải hiểu đặc điểm của double là gì trước tiên.

1.1 double là “Floating-Point Độ Chính Xác Gấp Đôi”

double là một số floating-point độ chính xác gấp đôi đại diện cho các giá trị bằng 64 bit (8 byte).
Như tên gọi “floating-point” gợi ý, double nội bộ xử lý các giá trị số như là các xấp xỉ.

Do đó, nó có những đặc điểm sau:

  • Có thể xử lý một dải giá trị cực rộng
  • Có thể thực hiện các phép tính có phần thập phân
  • Tuy nhiên, không thể biểu diễn mọi giá trị thập phân một cách chính xác

Việc “không thể biểu diễn mọi số thập phân một cách chính xác” sẽ được giải thích chi tiết sau, nhưng việc ghi nhớ rằng double không phải là万能 sẽ giúp phần còn lại dễ hiểu hơn nhiều.

1.2 Hình Ảnh Thực Tế về Những gì double Có Thể Đại Diện

Dải giá trị mà một double có thể biểu diễn là vô cùng lớn. Nói một cách tổng quát, nó có thể xử lý:

  • Các số rất nhỏ (ví dụ: 0.0000000001)
  • Các số rất lớn (cấp độ 10^300)
  • Các số thập phân thông thường trong cuộc sống hàng ngày

Ví dụ, tất cả các giá trị sau đều có thể được lưu trong một double:

double a = 3.14;
double b = 0.1;
double c = 123456789.987;

Đối với các phép tính như xử lý số học hàng ngày, các đại lượng vật lý, dữ liệu thống kê và tính toán tọa độ—nơi “khoảng đúng là đủ”double thường được dùng làm lựa chọn tiêu chuẩn.

1.3 Trong Java, Các Literal Thập Phân Mặc Định là double

Trong Java, khi bạn viết một literal số có dấu chấm thập phân, nó sẽ được coi là double theo mặc định trừ khi bạn chỉ định khác.

double x = 1.23;   // OK
float  y = 1.23;   // コンパイルエラー

Trong ví dụ trên, float y = 1.23; sẽ lỗi vì 1.23 được hiểu là một double.
Nếu bạn muốn nó được coi là float, bạn phải chỉ định rõ ràng như sau:

float y = 1.23f;

Hành vi này cho thấy trong Java, double là mặc định cho các phép tính thập phân.

1.4 Các Tình Huống Thường Gặp Khi Dùng double

double thường được dùng trong các tình huống như sau:

  • Khi bạn muốn kết quả phép chia là một số thập phân
  • Các phép tính như trung bình và tỷ lệ
  • Vẽ đồ thị và tính toán tọa độ
  • Tính toán khoa học và xử lý thống kê

Ngược lại, nó không phù hợp cho các phép tính tiền tệ hoặc xử lý thập phân nghiêm ngặt.
Điều này sẽ được giải thích chi tiết hơn sau như một phần của việc lựa chọn giữa doubleBigDecimal.

1.5 Những Điểm Chính Cần Nhớ Đầu Tiên

Để tóm tắt những gì bạn đã học được cho đến nay một cách thân thiện với người mới bắt đầu:

  • double là kiểu cơ bản cho các số thập phân
  • Nội bộ, các phép tính sử dụng giá trị xấp xỉ
  • Trong Java, các literal thập phân mặc định là double
  • Bạn phải cẩn thận khi độ chính xác là vấn đề quan trọng

2. Dải Giá Trị và Độ Chính Xác của double (Hướng Dẫn về Chữ Số Đáng Chú Ý)

Khi học kiểu double, một chủ đề không thể tránh được là câu hỏi “nó có thể đại diện cho các số một cách chính xác đến mức nào?”
Trong phần này, bạn sẽ học về dải số, độ chính xác và khái niệm chữ số đáng chú ý của double một cách dễ hiểu cho người mới bắt đầu.

2.1 Dải Giá Trị Số mà double Có Thể Đại Diện

double sử dụng 64 bit để biểu diễn giá trị, nó có thể xử lý một dải số cực rộng.

Khái niệm về dải này trông như sau:

  • Các số rất nhỏ: ví dụ, xuống tới khoảng 4.9 × 10^-324
  • Các số rất lớn: ví dụ, lên tới khoảng 1.8 × 10^308

Trong lập trình hàng ngày, bạn hiếm khi cần phải suy nghĩ về những giới hạn này.
Đối với hầu hết các mục đích thực tiễn, bạn có thể coi nó như việc xử lý một phạm vi “gần vô hạn”.

2.2 Chữ số có nghĩa khoảng 15 đến 17 chữ số

Khái niệm then chốt để hiểu độ chính xác của doublechữ số có nghĩa.

Một double thường được cho là có thể lưu trữ khoảng 15 đến 17 chữ số một cách chính xác.

Ví dụ, hãy xem xét các giá trị sau:

double a = 123456789012345.0;
double b = 1234567890123456.0;
  • a (15 chữ số) có thể được biểu diễn một cách chính xác
  • b (16+ chữ số) có thể có các chữ số thấp hơn bị làm tròn

Nói cách khác, khi số lượng chữ số tăng lên, độ chính xác chi tiết dần dần bị mất.

2.3 “Độ chính xác cao” không đồng nghĩa với “Số thập phân chính xác”

Đây là một hiểu lầm phổ biến của người mới bắt đầu.

double có độ chính xác cao
→ vì vậy nó có thể xử lý các số thập phân một cách chính xác

Điều này không phải luôn luôn đúng.

double thực sự là một kiểu có độ chính xác cao, nhưng điều đó có nghĩa là:
“nó có thể chính xác như một phép xấp xỉ tới một số lượng chữ số nhất định”
chứ không phải “mọi giá trị thập phân đều chính xác.”

Ví dụ, hãy xem xét phép tính này:

double x = 0.1 + 0.2;
System.out.println(x);

Nhiều người mong đợi 0.3, nhưng thực tế bạn có thể thấy một thứ gì đó như:

0.30000000000000004

Đây không phải là lỗi. Đó là cách double hoạt động.
Lý do là 0.10.2 không thể được biểu diễn một cách chính xác trong hệ nhị phân.

2.4 Tại sao chữ số có nghĩa lại quan trọng

Nếu bạn không hiểu chữ số có nghĩa, bạn có thể gặp phải các vấn đề như:

  • Kết quả hơi lệch
  • So sánh bằng == không khớp
  • Lỗi tích lũy trong tổng hoặc trung bình

Tất cả những điều này xuất phát từ thực tế rằng double là một kiểu xử lý giá trị xấp xỉ.

Ngược lại, trong các lĩnh vực mà “độ bằng nhau hoàn hảo” ít quan trọng hơn “độ chính xác thực tế”, chẳng hạn:

  • Vẽ đồ thị
  • Mô phỏng vật lý
  • Xử lý thống kê

double là một lựa chọn tuyệt vời.

2.5 Hiểu đúng dựa trên phạm vi và độ chính xác

Tóm lại:

  • double có thể biểu diễn một phạm vi giá trị cực kỳ rộng
  • Nó có khoảng 15–17 chữ số có nghĩa
  • Các số thập phân được xử lý như các phép xấp xỉ
  • Hãy cẩn thận khi yêu cầu độ chính xác nghiêm ngặt

3. Sự khác nhau giữa double và float (Bạn nên dùng cái nào?)

Khi làm việc với các số thập phân trong Java, doublefloat hầu như luôn được so sánh.
Cả hai đều có thể biểu diễn giá trị phần thập phân, nhưng có những khác biệt lớn về độ chính xác, trường hợp sử dụng và tính thực tiễn.

Trong phần này, chúng tôi tập trung vào các tiêu chí quyết định rõ ràng để người mới không bị kẹt.

3.1 Những khác biệt cơ bản giữa float và double

Hãy giữ so sánh đơn giản:

TypeBitsTypical PrecisionMain Use
float32-bitAbout 6–7 digitsLightweight / lower precision
double64-bitAbout 15–17 digitsStandard / higher precision

Như bạn thấy, double cung cấp độ chính xác cao hơn đáng kể.

3.2 Tại sao double là tiêu chuẩn trong Java

Trong Java, các literal thập phân (như 1.23) được coi là double theo mặc định.
Điều này chủ yếu vì:

  • Trên các CPU hiện đại, chi phí hiệu năng của double thường không đáng kể
  • Các lỗi do độ chính xác không đủ thường nghiêm trọng hơn
  • double tốt hơn cho khả năng đọc và an toàn trong mã thông thường

Do đó, trong Java, cách tiếp cận phổ biến là: sử dụng double trừ khi bạn có lý do mạnh mẽ để không dùng.

3.3 Khi nào bạn nên dùng float

Vậy float có vô dụng không?
Không—nó có thể hiệu quả tùy thuộc vào ngữ cảnh.

Các tình huống điển hình mà float được chọn bao gồm:

  • Khi bạn cần giảm thiểu việc sử dụng bộ nhớ một cách cực kỳ
  • Khi xử lý các mảng số lượng lớn (ví dụ, xử lý ảnh)
  • Khi ưu tiên tốc độ hơn độ chính xác trong trò chơi hoặc tính toán 3D

Tuy nhiên, đối với các ứng dụng doanh nghiệp hoặc web,
hiếm khi có lý do mạnh mẽ để sử dụng float.

3.4 Quy tắc đơn giản cho người mới

Nếu bạn không chắc nên dùng cái nào, hãy nhớ quy tắc này:

  • Nếu còn nghi ngờ, hãy dùng double
  • Chỉ dùng float khi các ràng buộc về bộ nhớ/hiệu năng là nghiêm ngặt

Đặc biệt khi đang học, thường hiệu quả hơn khi tránh các vấn đề về độ chính xác đặc thù của float và xây dựng hiểu biết bằng double.

3.5 Tại sao Hiểu Sự Khác Biệt Này Quan Trọng

Biết sự khác biệt giúp trong các tình huống như:

  • Hiểu ý định khi đọc mã của người khác
  • Ngăn ngừa mất độ chính xác không cần thiết
  • Tránh các lỗi tính toán số trước khi chúng xảy ra

4. Cách Sử Dụng Cơ Bản của double (Khai báo, Tính toán, Xuất Kết Quả)

Bây giờ hãy xác nhận cách sử dụng cơ bản của double bằng mã thực tế.
Nếu bạn hiểu quy trình “khai báo → tính toán → xuất kết quả”, bạn đã nắm vững các nền tảng cơ bản.

4.1 Khai báo và Gán Giá trị cho một double

Bạn khai báo một double giống như các kiểu nguyên thủy khác:

double x = 1.5;
double y = 2.0;

Khi gán giá trị thập phân, bạn không cần bất kỳ hậu tố đặc biệt nào.
Bạn cũng có thể gán một giá trị nguyên; nó sẽ tự động được chuyển đổi thành double.

double a = 10;   // Treated as 10.0

4.2 Bốn Phép Toán Cơ Bản

Với double, bạn có thể trực tiếp sử dụng phép cộng, phép trừ, phép nhân và phép chia:

double a = 5.0;
double b = 2.0;

double sum = a + b;      // addition
double diff = a - b;     // subtraction
double product = a * b;  // multiplication
double quotient = a / b; // division

Tất cả các kết quả cũng là double, vì vậy bạn có thể tự nhiên xử lý các kết quả thập phân.

4.3 Lưu Ý Quan Trọng Khi Chia

Điểm then chốt là phép chia sẽ trở thành phép tính thập phân chỉ khi ít nhất một phía là double.

double result1 = 1 / 2;    // result is 0.0
double result2 = 1 / 2.0;  // result is 0.5

1 / 2 sử dụng các số nguyên ở cả hai phía, vì vậy nó thực hiện phép chia nguyên trước.
Để tránh điều này, hãy thực hiện một trong các cách sau:

  • Biến một toán hạng thành double
  • Sử dụng ép kiểu rõ ràng
    double result = (double) 1 / 2;
    

4.4 In Kết Quả Tính Toán

Bạn có thể in trực tiếp giá trị double bằng System.out.println():

double value = 3.14159;
System.out.println(value);

Tuy nhiên, in nguyên trạng có thể hiển thị nhiều chữ số hơn bạn mong muốn.

4.5 In Với Số Chữ Số Cố Định

Nếu bạn muốn đầu ra gọn gàng hơn, printf rất tiện dụng:

double value = 3.14159;
System.out.printf("%.2f%n", value);

Lệnh này in giá trị với hai chữ số sau dấu thập phân.

  • %.2f : 2 chữ số sau dấu thập phân
  • %n : xuống dòng

Đối với các giao diện người dùng, nên luôn kiểm soát số chữ số hiển thị.

4.6 Tóm Tắt Các Kiến Thức Cơ Bản Về Tính Toán với double

Những điểm quan trọng rút ra từ phần này:

  • double hỗ trợ tính toán thập phân một cách tự nhiên
  • Cẩn thận khi chia giữa các số nguyên
  • Kiểm soát số chữ số hiển thị khi in kết quả

5. Sai Lầm Thường Gặp #1: Tại sao Lỗi Độ Chính Xác Xảy Ra

Một trong những trải nghiệm gây bối rối đầu tiên khi dùng double là:
“kết quả không khớp với những gì tôi mong đợi.”

Đây không phải là lỗi của Java. Đó là đặc tính cốt lõi của kiểu double.

5.1 Một Ví Dụ Kinh Điển Về Lỗi Độ Chính Xác

Hãy xem một ví dụ nổi tiếng:

double x = 0.1 + 0.2;
System.out.println(x);

Nhiều người mong đợi 0.3, nhưng thực tế bạn có thể thấy:

0.30000000000000004

Khi thấy điều này, bạn có thể nghĩ “có gì đó sai” hoặc “đây có phải là lỗi không?”
Nhưng đây là kết quả đúng cho double.

5.2 Tại sao Loại Lỗi Này Xảy Ra

Lý do là double biểu diễn số bằng hệ nhị phân.

Con người dùng hệ thập phân (cơ số 10), nhưng máy tính bên trong dùng hệ nhị phân (cơ số 2).
Vấn đề then chốt là:

  • Nhiều số thập phân cơ số 10 không thể biểu diễn bằng một số hữu hạn các chữ số nhị phân
  • Vì vậy hệ thống lưu giá trị xấp xỉ gần nhất

0.10.2 không kết thúc trong hệ nhị phân, nên chúng được lưu dưới dạng các giá trị “rất gần” nhưng không hoàn toàn giống với các giá trị thập phân chính xác.

Cộng các giá trị xấp xỉ này sẽ cho ra kết quả như 0.30000000000000004.

5.3 Luôn Giả Định Rằng Lỗi Có Thể Xảy Ra

Tư duy quan trọng nhất là:

Với double, lỗi độ chính xác là không thể tránh khỏi

Lỗi trở nên đặc biệt đáng chú ý trong các trường hợp như:

  • Thực hiện các phép cộng/trừ lặp lại nhiều lần
  • Các phép tính có liên quan đến phép chia
  • Khi bạn cần độ chính xác nghiêm ngặt đến nhiều chữ số thập phân

Tóm lại, double không được thiết kế cho “số thập phân hoàn toàn chính xác.”

5.4 Khi Lỗi Thường Không Quan Trọng

Mặt khác, có rất nhiều trường hợp mà lỗi độ chính xác hiếm khi là một vấn đề thực tế.

Ví dụ:

  • Đồ thị và hoạt hình
  • Giá trị cảm biến và dữ liệu thống kê
  • Tính toán khoa học
  • Trò chơi và mô phỏng

Trong những lĩnh vực này, điều quan trọng hơn so với sự bằng nhau tuyệt đối là khả năng tính toán với độ chính xác thực tế.

Đó là nơi double tỏa sáng.

5.5 Cách Bạn Nên Xử Lý Lỗi Độ Chính Xác

Cố gắng loại bỏ hoàn toàn lỗi thường làm hỏng thiết kế của bạn.
Thay vào đó, hãy tuân theo các nguyên tắc sau:

  • Chấp nhận rằng lỗi “có thể xảy ra”
  • Sử dụng các phương pháp so sánh/đánh giá phù hợp
  • Áp dụng các cách tiếp cận khác khi lỗi không thể chấp nhận được

6. Sai Lầm Thường Gặp #2: Sử Dụng “==” Để So Sánh Giá Trị double Là Nguy Hiểm

Sau khi hiểu về lỗi độ chính xác, vấn đề tiếp theo mà hầu hết mọi người gặp phải là:
“Tại sao việc so sánh không hoạt động như mong đợi?”

Một lỗi phổ biến của người mới là so sánh các giá trị double bằng ==.

6.1 Điều Gì Xảy Ra Khi Bạn Sử Dụng “==”

Xem đoạn mã sau:

double a = 0.1 + 0.2;
double b = 0.3;

System.out.println(a == b);

Một cách trực giác, bạn có thể mong đợi true, nhưng kết quả có thể là false.
Điều này gây ra bởi lỗi độ chính xác đã được giải thích ở trên.

  • a gần với 0.30000000000000004
  • b là một xấp xỉ khác đại diện cho 0.3

Vì các giá trị này không giống hệt ở mức bit, == báo cáo chúng là khác nhau.

6.2 Quy Tắc Vàng Khi So Sánh Giá Trị double

Có một quy tắc cơ bản mà bạn luôn nên nhớ:

Không bao giờ so sánh giá trị double để kiểm tra bằng nhau một cách chính xác

Toán tử == kiểm tra xem các biểu diễn nội bộ có hoàn toàn giống nhau không, chứ không phải giá trị có “đủ gần” hay không.

Điều này khiến nó không phù hợp cho các số dấu chấm động.

6.3 So Sánh Với Epsilon (Khoảng Sai)

Khi so sánh các giá trị double, bạn nên xác định một ngưỡng cho
“độ gần đủ để chấp nhận”.

Ngưỡng này được gọi là epsilon (khoảng sai).

double a = 0.1 + 0.2;
double b = 0.3;

double epsilon = 1e-9;

if (Math.abs(a - b) < epsilon) {
    System.out.println("Approximately equal");
}

Cách tiếp cận này kiểm tra xem:

  • Sự chênh lệch tuyệt đối đủ nhỏ
  • Các giá trị có thể được coi là bằng nhau cho các mục đích thực tiễn

6.4 Làm Thế Nào Để Chọn Epsilon?

Một câu hỏi phổ biến của người mới là:
“Epsilon nên lớn bao nhiêu?”

Ý tưởng rất đơn giản. Dựa vào:

  • Quy mô của các giá trị bạn đang xử lý
  • Mức độ lỗi bạn có thể chấp nhận

Các quy tắc tham khảo thường gặp bao gồm:

  • 1e-9 : an toàn cho nhiều phép tính chung
  • 1e-12 : rất nghiêm ngặt
  • 1e-6 : phù hợp cho hiển thị hoặc ước tính sơ bộ

Không có một giá trị đúng duy nhất.
Hãy chọn dựa trên trường hợp sử dụng của bạn.

6.5 Sự Khác Biệt So Với BigDecimal

Nhìn về phía trước một chút, lưu ý sự khác biệt trong triết lý:

  • double : hoạt động trong khi chấp nhận các lỗi nhỏ
  • BigDecimal : thiết kế không cho phép lỗi

Nếu việc so sánh cảm thấy “quá phức tạp,” có thể là dấu hiệu rằng double không phải là lựa chọn phù hợp cho nhiệm vụ đó.

6.6 Tóm Tắt Các Quy Tắc So Sánh

  • Không sử dụng ==
  • So sánh bằng cách dùng chênh lệch tuyệt đối
  • Chọn epsilon dựa trên ngữ cảnh
  • Sử dụng các kiểu dữ liệu khác nếu yêu cầu độ chính xác nghiêm ngặt

7. Sai Lầm Thường Gặp #3: Phép Chia Nguyên Số Cho Kết Quả Bằng 0

Ngay cả khi sử dụng double, bạn có thể thấy kết quả bất ngờ trở thành 0.
Đây là một trong những lỗi phổ biến nhất của người mới.

Nguyên nhân rất đơn giản: phép tính được thực hiện bằng các số nguyên.

7.1 Tại Sao Phép Chia Nguyên Số Xảy Ra

Trong Java, kiểu của các toán hạng quyết định cách thực hiện phép tính.
Xem đoạn mã sau:

double result = 1 / 2;
System.out.println(result);

Kết quả là:

0.0

Vì cả 12 đều là int, Java thực hiện phép chia nguyên trước, cho ra 0, sau đó được chuyển sang double.

7.2 Cách Thực Hiện Phép Chia Thập Phân Đúng

Tránh phép chia nguyên rất dễ.
Đảm bảo ít nhất một toán hạng là double.

double result1 = 1 / 2.0;
double result2 = 1.0 / 2;
double result3 = (double) 1 / 2;

Tất cả các đoạn mã này cho ra:

0.5

7.3 Một Sai Lầm Thực Tế Thường Gặp

Loại mã này thường xuất hiện trong các ứng dụng thực tế:

int total = 5;
int count = 2;

double average = total / count;

Mặc dù trông có vẻ đúng, kết quả lại là 2.0.
Cách tính trung bình đúng là:

double average = (double) total / count;

7.4 Các Quy Tắc Người Mới Nên Nhớ

  • Luôn chú ý đến kiểu dữ liệu khi thực hiện phép chia
  • Phép chia số nguyên cho số nguyên cho ra một số nguyên
  • Nhận kết quả dưới dạng double không đủ

7.5 Tóm Tắt Bẫy Phép Chia Nguyên

  • Nguyên nhân là do kiểu toán hạng và thứ tự đánh giá
  • Sử dụng double ngay từ đầu của phép tính
  • Đặc biệt cẩn thận với các phép tính trung bình và tỷ lệ

8. Chuyển Đổi Giữa String và double (Phổ Biến Nhất Trong Thực Tiễn)

Trong các ứng dụng thực tế, bạn hiếm khi hard-code giá trị double.
Thường xuyên hơn, bạn cần chuyển đổi chuỗi (String) thành số.

Các ví dụ điển hình bao gồm:

  • Giá trị nhập từ biểu mẫu
  • Các tệp CSV hoặc JSON
  • Các tệp cấu hình
  • Phản hồi API

Phần này giải thích các cách an toàn để chuyển đổi giữa Stringdouble.

8.1 Chuyển Đổi String sang double

8.1.1 Double.parseDouble()

Phương pháp phổ biến và cơ bản nhất:

String text = "3.14";
double value = Double.parseDouble(text);

Nếu chuỗi là một số hợp lệ, việc chuyển đổi sẽ thành công.

8.1.2 Xử Lý Các Chuỗi Không Hợp Lệ

Nếu chuỗi không phải là số, một ngoại lệ sẽ được ném:

String text = "abc";
double value = Double.parseDouble(text); // throws exception

Điều này ném ra một NumberFormatException.
Trong thực tế, luôn sử dụng try-catch:

try {
    double value = Double.parseDouble(text);
} catch (NumberFormatException e) {
    // error handling
}

8.2 Sự Khác Biệt Giữa Double.valueOf()

Double.valueOf() là một phương pháp khác để chuyển đổi chuỗi thành giá trị số.

Double value = Double.valueOf("3.14");

Sự khác biệt giữa hai phương pháp là:

  • parseDouble → trả về một double nguyên thủy
  • valueOf → trả về một đối tượng Double wrapper

Đối với các phép tính số thông thường, parseDouble là đủ.
Khi làm việc với các collection như List<Double>, valueOf thường được sử dụng.

8.3 Chuyển Đổi double sang String

Chuyển đổi một double sang chuỗi cũng rất phổ biến.

double value = 3.14;
String text = String.valueOf(value);

Một tùy chọn phổ biến khác là:

String text = Double.toString(value);

Cả hai cách đều an toàn. Hãy chọn dựa trên phong cách hoặc quy ước dự án.

8.4 Luôn Định Dạng Chuỗi Khi Hiển Thị

Khi hiển thị số cho người dùng,
luôn sử dụng đầu ra đã định dạng.

double value = 3.14159;
String text = String.format("%.2f", value);

Điều này định dạng giá trị thành hai chữ số thập phân.

8.5 Các Điểm Chính Khi Chuyển Đổi String

  • Luôn dự đoán đầu vào không hợp lệ và xử lý ngoại lệ
  • Tách biệt các phép tính nội bộ khỏi việc định dạng hiển thị
  • Luôn kiểm soát số chữ số thập phân khi hiển thị giá trị

Phần tiếp theo giải thích các giá trị double đặc biệt: NaN và Infinity.
Nếu không hiểu chúng, việc gỡ lỗi các vấn đề số học sẽ rất khó khăn.

9. Các Giá Trị Đặc Biệt trong Lớp Double (NaN / Infinity)

Kiểu double bao gồm các giá trị đặc biệt không phải là số thông thường.
Hiểu chúng là cần thiết cho các chương trình vững chắc.

9.1 NaN là gì (Not a Number)?

NaN đại diện cho một kết quả số không xác định.
Nó xuất hiện trong các phép tính như:

double value = 0.0 / 0.0;
System.out.println(value);

Kết quả là:

NaN

9.2 Infinity là gì?

Khi kết quả phép chia tràn, Java tạo ra Infinity.

double value = 1.0 / 0.0;
System.out.println(value);

Kết quả là:

Infinity

Nếu tử số là số âm, kết quả sẽ là -Infinity.

9.3 Cách kiểm tra NaN và Infinity

Các giá trị này không thể kiểm tra một cách đáng tin cậy bằng ==.
Luôn sử dụng các phương thức chuyên dụng:

double value = 0.0 / 0.0;

if (Double.isNaN(value)) {
    System.out.println("Value is NaN");
}

if (Double.isInfinite(value)) {
    System.out.println("Value is infinite");
}

9.4 Tại sao việc phát hiện sớm lại quan trọng

Nếu NaN hoặc Infinity lan truyền qua các phép tính, chúng có thể gây ra:

  • Tất cả các kết quả tiếp theo trở thành NaN
  • Giá trị không hợp lệ hiển thị trong giao diện người dùng
  • Logic ứng dụng bị hỏng

Phát hiện các giá trị bất thường càng sớm càng tốt.

10. Sử dụng BigDecimal cho tiền và các phép tính chính xác

Như đã thấy, double rất tiện lợi,
nhưng nó không thể hoàn toàn tránh lỗi làm tròn.

Do đó, nó không phù hợp cho:

  • Các phép tính tiền tệ
  • Quản lý điểm hoặc số dư
  • Yêu cầu làm tròn nghiêm ngặt

10.1 BigDecimal là gì?

BigDecimal là một lớp xử lý các số thập phân một cách chính xác trong hệ cơ số 10.
Nó được thiết kế để tránh mất độ chính xác.

BigDecimal a = new BigDecimal("0.1");
BigDecimal b = new BigDecimal("0.2");

BigDecimal result = a.add(b);
System.out.println(result); // 0.3

10.2 Quy tắc quan trọng khi sử dụng BigDecimal

Tạo một BigDecimal trực tiếp từ double sẽ mang theo các lỗi làm tròn.

new BigDecimal(0.1); // Not recommended

Luôn tạo nó từ một chuỗi thay vì:

new BigDecimal("0.1"); // Correct

10.3 Lựa chọn giữa double và BigDecimal

  • Hiệu năng và đơn giản → double
  • Độ chính xác trên hết → BigDecimal

Đừng ép buộc mọi thứ vào một kiểu duy nhất.
Chọn dựa trên mục đích sử dụng.

11. Tóm tắt: Sử dụng Java double đúng cách

Hãy tóm tắt các điểm chính của bài viết:

  • double là kiểu số thập phân cơ bản của Java
  • Lỗi làm tròn là một phần của đặc tả
  • Sử dụng độ dung sai khi so sánh
  • Cẩn thận với phép chia nguyên
  • Sử dụng BigDecimal cho các phép tính chính xác

Khi bạn hiểu cách double hoạt động,
lập trình số học sẽ trở nên ít đáng sợ hơn nhiều.

12. Câu hỏi thường gặp (FAQ)

Q1. Độ chính xác của Java double là bao nhiêu chữ số?

Khoảng 15–17 chữ số có nghĩa. Các giá trị thập phân được xử lý dưới dạng xấp xỉ.

Q2. Nên dùng double hay float?

Trong hầu hết các trường hợp, nên dùng double trừ khi bạn có lý do mạnh mẽ để chọn float.

Q3. Có nên dùng double cho tiền tệ không?

Có. Điều này không được khuyến nghị. Hãy dùng BigDecimal để tính toán tiền an toàn.

Q4. Có thể dùng “==” để so sánh giá trị double không?

Không. Hãy dùng độ dung sai (epsilon) thay thế.

Q5. Nên xử lý NaN và Infinity như thế nào?

Sử dụng Double.isNaN()Double.isInfinite() để phát hiện sớm và xử lý chúng một cách rõ ràng.