CodeGym /Các khóa học /C# SELF /Vấn đề về độ chính xác và các giá trị đặc biệt

Vấn đề về độ chính xác và các giá trị đặc biệt

C# SELF
Mức độ, Bài học
Có sẵn

1. Giới thiệu

Nghe có vẻ máy tính "thông minh" thì 0.1 + 0.2 chắc chắn phải ra 0.3 chứ nhỉ? Nhưng thực tế không hẳn vậy đâu. Cùng xem ví dụ đơn giản nhé.

double x = 0.1;
double y = 0.2;
double sum = x + y;
Console.WriteLine(sum); // Chương trình sẽ in ra gì nhỉ?
Cộng số thực: mong đợi 0.3, nhưng thực tế thì sao?

Giờ thử so sánh với 0.3 xem:

Console.WriteLine(sum == 0.3); // Ở đây sẽ là true hay false nhỉ?

Nếu bạn thấy False, đừng ngạc nhiên nha!

Lý do nằm ở cách lưu số trong bộ nhớ

Máy tính làm việc với số ở hệ nhị phân. Nhưng không phải mọi số thập phân đều có thể biểu diễn chính xác bằng số nhị phân, giống như 1/3 không thể viết chính xác bằng số thập phân (0.333...). Ví dụ, 0.1 ở hệ nhị phân là một số thập phân vô hạn, nên phải "làm tròn" khi lưu trữ.

Nói nôm na, double đôi khi "giả vờ" lưu chính xác số của bạn, nhưng thực ra chỉ lưu một giá trị gần đúng thôi.

2. Những "lỗi lạ" khi làm toán với double?

Cùng xem vài ví dụ thực tế nhé.

Ví dụ 1. "Ảo thuật" kinh điển 0.1 + 0.2

double a = 0.1;
double b = 0.2;
double sum = a + b;
Console.WriteLine(sum);            // 0.30000000000000004
Console.WriteLine(sum == 0.3);     // False

Máy tính in ra không phải 0.3, mà là 0.30000000000000004. Nhìn thì nhỏ thôi, nhưng nếu bạn làm tài chính chẳng hạn — là vấn đề lớn rồi đó.

Ví dụ 2. Cộng lặp nhiều lần

double result = 0;
for (int i = 0; i < 10; i++)
{
    result += 0.1;
}
Console.WriteLine(result); // 0.9999999999999999

Muốn ra 1.0 — lại bị thiếu chút xíu. Lý do vẫn là làm tròn trong double đó.

Tại sao chuyện này quan trọng ngoài đời thực

Nhiều người nghĩ: "Sao cũng được, sai chút xíu có sao đâu!" Thử xem ví dụ trong thanh toán nhé.

Giả sử ngân hàng online của bạn cộng 100 giao dịch mỗi giao dịch 0.1 euro. Nếu chương trình của bạn "mất" mỗi lần một phần trăm nghìn euro, thì ở quy mô ngân hàng là "bốc hơi" tiền thật rồi đó. Lúc này kế toán sẽ hỏi bạn ngay: "Tiền của tụi tui đâu rồi?!"

3. So sánh số thực cho đúng cách

Vì double thường không lưu đúng giá trị bạn mong đợi, so sánh trực tiếp bằng == dễ bị "hố". Thay vào đó, nên so sánh giá trị tuyệt đối của hiệu hai số với một số rất nhỏ (epsilon).

Ví dụ so sánh có sai số cho phép

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

if (Math.Abs(a - b) < epsilon)
{
    Console.WriteLine("Gần như bằng nhau!"); // So sánh kiểu này an toàn hơn
}

Ở đây mình nói: "Nếu hiệu hai số nhỏ hơn một phần triệu, coi như bằng nhau rồi".

4. Giá trị đặc biệt của double: Infinity, NaN, -Infinity

Kiểu double không chỉ lưu số, mà còn có các giá trị đặc biệt nữa. Chúng xuất hiện trong các trường hợp mà thầy toán chắc chắn cấm làm luôn.

Vô cực (Infinity)

Chia 1 cho 0 thì sao nhỉ?

double result = 1.0 / 0.0;
Console.WriteLine(result); // Infinity

Trong C# (và nhiều ngôn ngữ khác) chia cho 0 với double không bị lỗi đâu! Kết quả sẽ là giá trị đặc biệt "vô cực dương".

Âm vô cực (-Infinity)

Nếu chia số âm cho 0, bạn sẽ nhận được âm vô cực:

double result = -1.0 / 0.0;
Console.WriteLine(result); // -Infinity

"Không phải số" (NaN — Not a Number)

Nếu làm gì đó "dị" hơn, ví dụ tính căn bậc hai của số âm:

double result = Math.Sqrt(-1);
Console.WriteLine(result); // NaN

Hoặc chia 0.0 / 0.0:

double result = 0.0 / 0.0;
Console.WriteLine(result); // NaN

NaN — là "mọi thứ mà ngoài đời không thể gọi là số".

Kiểm tra giá trị đặc biệt

Trong C# có hàm kiểm tra giá trị đặc biệt:

Console.WriteLine(double.IsInfinity(result));    // true nếu là vô cực
Console.WriteLine(double.IsNaN(result));         // true nếu là NaN

Bảng: double phản ứng thế nào với các phép toán lạ

Phép toán Kết quả Lưu trong double là gì
1.0 / 0.0
Infinity +∞
-1.0 / 0.0
-Infinity -∞
0.0 / 0.0
NaN Không phải số
Math.Sqrt(-1)
NaN Không phải số
2
Nhiệm vụ
C# SELF, mức độ, bài học
Đã khóa
Sự khác biệt về độ chính xác của số thực
Sự khác biệt về độ chính xác của số thực
2
Nhiệm vụ
C# SELF, mức độ, bài học
Đã khóa
So sánh sử dụng epsilon
So sánh sử dụng epsilon
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION