số thực thì máy tính tính cũng chỉ chứa được tới x chữ số thôi :V float
thì chứa ít chữ số hơn, 24 chữ số nhị phân ~7 chữ số thập phân. double
chứa nhiều chữ số hơn, 53 chữ số nhị phân ~16 chữ số thập phân.
1/3 ở hệ thập phân ko biểu diễn được vì 10 ko chia hết cho 3, nên 1/3 = 0.3333… Tương tự 1/7 1/6 1/9 1/13 v.v… cũng ko biểu diễn được ở hệ thập phân. Hệ nhị phân tương tự :V còn khó biểu diễn tròn số như hệ thập phân :V 0.1, 0.2, 0.3 đều ko biểu diễn tròn số được ở hệ nhị phân.
để biểu diễn số vô hạn tuần hoàn này thì ta có thể viết () cho dãy tuần hoàn :V
ví dụ
- 1/3 = 0.(3) = 0.0 + 3/9
- 1/6 = 0.1(6) = 0.1 + 6/90
- 1/33 = 0.(03) = 3/99
để ý thấy là có n số mở ngoặc ở trong thì lấy số đó chia cho n số 9 hay (10^n - 1) là ra :V ngoài ra còn phải đếm nó cách dấu thập phân m số thì phải chia cho 10^m nữa.
vậy ở hệ nhị phân thì
- 0.1 = 0.0(0011): trong () có 4 chữ số, giá trị là 0011 = 3, vậy lấy 3 / (2^4-1) = 3/15 = 0.2, mà () cách dấu . thập phân 1 số, vậy chia 2^1 là chia 2 nữa là = 0.1
- 0.2 = 0.(0011): trong () có 4 chữ số, giá trị là 0011 = 3, vậy lấy 3 / (2^4-1) = 3/15 = 0.2, () cách dấu . thập phân 0 số khỏi cần chia, giá trị là 0.2
- 0.3 = 0.0(1001): trong () có 4 chữ số, giá trị là 1001 = 9, vậy lấy 9 / (2^4-1) = 9/15 = 0.6, mà () cách dấu . thập phân 1 số, vậy chia 2^1 là chia 2 nữa là = 0.3
viết ra dưới dạng vô hạn
0.1 = 0.0(0011) = 0.000110011001100110011001100110011...
0.2 = 0.(0011) = 0.001100110011001100110011001100110...
0.3 = 0.0(1001) = 0.010011001100110011001100110011001...
máy tính ko lấy dấu . thập phân xa thế kia, ví dụ ở hệ thập phân ta viết 0.01234 thì có thể viết lại ở dạng 1.234 x 10^-2, làm sao cho số trước thập phân < 10. Tương tự ở hệ nhị phân, số trước dấu . phải là số < 2, ở đây chỉ có thể là số 1. Vậy viết lại
0.1 = 0.0(0011) = 1. 1001 1001 1001 1001 1001 1001 1001 1... x 2^-4
0.2 = 0.(0011) = 1. 1001 1001 1001 1001 1001 1001 1001 1... x 2^-3
0.3 = 0.0(1001) = 1. 0011 0011 0011 0011 0011 0011 0011 0... x 2^-2
do số trước dấu thập phân luôn là số 1 nên có thể bỏ qua, với float chỉ lưu được 23 chữ số sau dấu ., là
1001 1001 1001 1001 1001 101 với 0.1
1001 1001 1001 1001 1001 101 với 0.2
0011 0011 0011 0011 0011 010 với 0.3
lưu ý ở đây vì phần bit bỏ đi là 1001 thì bit đầu tiên bị bỏ đi là bit 1 nên nó sẽ làm tròn 100 thành 101. Tương tự 0.3 có 0011 cũng bị làm tròn thành 010.
với double
thì nó lưu được tới 52 bit sau dấu .
1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1010 với 0.1
1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1010 với 0.2
0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 với 0.3
lưu ý 0.1 và 0.2 sẽ được làm tròn lên: 10011 thành 1010, còn 0.3 bị làm tròn xuống: 00110 thành 0011
đây là lý do dẫn đến so sánh == bị sai ở double vì 0.1 0.2 làm tròn lên thành số > 0.1 và 0.2, còn 0.3 bị làm tròn xuống thành số < 0.3, nên 0.1 + 0.2 > 0.3 ở double. Còn float thì có thể đúng vì cả 3 số đều được làm tròn lên (thật ra còn phải làm phép tính 0.1 + 0.2 nữa mà viết dài lười quá :V)
edit: ko chắc cái quy luật làm tròn của float
và double
lắm, tại có nhiều quy luật :V nhưng ví dụ quy luật gặp 0 thì bỏ gặp 1 thì +1 vào này là đủ để thấy là do làm tròn nên có sai số rồi nha :V