30/09/2018, 17:28

[C/C++] Thắc mắc sai số trong việc chuyển đổi kiểu

Chào A/c!
em vừa gặp trường hợp này nên thắc mắc như sau:
em có số a , b trong hình.

  • c là thương a chia b.
  • d là thương a chia b nhưng do em nhập
  • e kiểu int được gán bằng d sau khi ép kiểu int
  • f kiểu int được gán bằng c sau khi ép kiểu int

Với a, b, c, d là kiểu double.
Sau khi chạy code thì kết quả lại ra e khác với f:

  • e = 265182525
  • f = 265182524
    Em có thử lại thấy c - d = 0 nhưng khi so sánh c với d thì lại khác nhau.
    Em thắc mắc sao e và f lại khác nhau? Mong A/c thông não cho em với ạ!

viết 19:40 ngày 30/09/2018

vì số thực ko biểu diễn chính xác được.

vd 10/3 = 3.33333… ko cách nào ghi/biểu diễn chính xác được ở hệ thập phân. Thường thì chúng ta chỉ lấy tới bao nhiêu số sau dấu thập phân thôi, ví dụ 10/3 ~ 3.333, ở đây ta lấy 3 số sau dấu thập phân, kể cả số trước dấu thập phân thì có nghĩa độ chính xác là 4 số (4 digit)

số thập phân có thể biểu diễn dưới dạng khoa học (scientific), ví dụ số A vô ga drô = 6.023 x 10^23. Chuẩn là chỉ có 1 số khác 0 đứng trước dấu thập phân, còn độ lớn thì để ở phần số mũ. 6.023 tạm gọi là độ chính xác (mantissa / significant bits, dịch là độ chính xác cũng ko đúng lắm nhưng mà dịch tạm vậy) của số Avogadro, 10^23 tạm gọi là độ lớn của số Avogadro.

tới đây thì ta có thể lưu số thập phân theo 3 phần: 1 phần là độ chính xác, 1 phần là độ lớn, 1 phần là dấu. Ví dụ ta chỉ lưu được 10 ô số, thì chia ra 1 ô là dấu, 3 ô độ lớn và 7 ô độ chính xác thì số Avogadro được lưu dưới dạng: [+][023][6023000].

tương tự cho hệ nhị phân:

  • float gồm 32 bit: 1 bit dấu, 8 bit độ lớn, 23 bit độ chính xác.
  • double gồm 64 bit: 1 bit dấu, 11 bit độ lớn, 52 bit độ chính xác.

với hệ nhị phân thì có phần đặc biệt là nếu viết theo kiểu khoa học thì bit duy nhất trước dấu chấm (phẩy) luôn luôn là bit 1, nên ko cần lưu bit này mà chỉ cần lưu những bit sau dấu thập phân, nhưng khi tính độ chính xác thì phải tính tới bit này, vd float có 23 bit độ chính xác nhưng thật ra nó gồm 24 bit.

vì double chỉ lưu được 52+1=53 bit độ chính xác, chuyển qua hệ thập phân là 2^53 ~ 10^15.95 ~ chỉ lưu được tầm 15-16 ô số.

double a = 23118159385601278000.0, chuyển qua hệ khoa học là 2.3118159385601278x10^19 cần tới 17 ô số cho độ chính xác, nên nó sẽ ko được lưu chính xác. Có thể số 8 cuối cùng ko phải là 8 mà là số bất kì. Vì vậy khi lấy a/b sẽ ko cho ra số đích xác như số d nhập vào. Kết quả là 1 số rất gần d, nhưng nhỏ hơn d, vd là 265182524.9999999. Khi chuyển thành int thì chỉ giữ phần nguyên là 265182524, trong khi print ra thì nó lại được làm tròn thành 25. Hiệu c - d cũng là 1 số gần bằng 0 chứ ko phải 0. Khi in ra cũng được làm tròn thành 0.

tl,dr: đừng bao giờ tin vào độ chính xác 100% trên số thực.

Dr Sung viết 19:35 ngày 30/09/2018

ồ Thanks a

Mai Anh Dũng viết 19:40 ngày 30/09/2018

Nếu @Dr_Sung thấy câu trả lời của @tntxtnt đúng và đầy đủ thì nên bấm vào trên câu trả lời của @tntxtnt để chấp nhận câu trả lời của @tntxtnt

Bài liên quan
0