Tại sao (int)((0.1 + 0.7) *10) = 7 mà int((0.2 + 0.6) * 10) = 8?

Tại sao (int)((0.1 + 0.7) *10) = 7 mà int((0.2 + 0.6) * 10) = 8 ?

Mình đã search rồi mà vẫn chưa hiểu.

2 Likes

Các dấu ngoặc đã gõ kỹ chưa? :sweat:

Tại vì trong số thực dấu chấm động máy tính không thể biểu diễn chính xác 0.1. 0.1 trong máy tính có dạng: 0000.00011...., tương tự, 0.7 ở dạng nhị phân là: 0.1011.... Khi 0.1+0.7 thực chất sẽ là:0.7999999... và khi ép kiểu (int)(0.79999...*10) nó sẽ lấy phần nguyên của số thực đó tức là 7. tương tự với các số 0.20.8

5 Likes

Edit rồi bác . sr.

còn 2 số 0.2 + 0.6 nó biểu diễn dưới dạng binary đúng hả bác ?

ko bạn, 0.2 và 0.6 cũng không biểu diễn chính xác được.

2 Likes

vậy sao kết quả của nó lại hiển thị đúng là 8 vậy.

vì nó ra kết quả lớn hơn.

ví dụ 0.1 + 0.7 = 0.799999999999999999
còn 0.2 + 0.6 = 0.80000000000000001

em hiểu nó như là ở hệ cơ số 10 ta ko thể biểu diễn chính xác 1/3 vậy. Ta chỉ chứa được tới độ chính xác nào đó thôi, ví dụ là 3 chữ số đi, thì 1/3 biểu diễn là 0.333, còn 2/3 biểu diễn là 0.667. Vì vậy 1/3 + 2/3 = 1.001 lớn hơn 1. Còn 1/3 * 3 thì lại ra 0.999 bé hơn 1. int(x) là lấy phần nguyên của x. Nếu lấy phần nguyên của 1/3 + 2/3 thì ta được 1, còn 1/3 * 3 lại ra 0.

11 Likes

Yep. Đã hiểu vấn đề .

Hỏi ngu cái:
“1/3 biểu diễn là 0.333, còn 2/3 biểu diễn là 0.667” cái này có phải java quy định ko ạ?

“1/3 + 2/3 thì ta được 1, còn 1/3 * 3 lại ra 0”
Em có thử:
(int) (1/3 + 2/3) = 0
(int) (1/3 *3) = 0
Đều ra 0??

Thế này em nhé. Trong nhiều ngôn ngữ lập trình thì những số mà kiểu số nguyên ví dụ như kiểu int nó sẽ lấy phần trước dấu phẩy, còn phần sau dấu phẩy nó sẽ bỏ đi. Trong ví dụ của em thì 1/3 = 0.333 nó chỉ lấy số 0 thôi, vứt ,333 đi.

Còn cái câu làm tròn thì là như này. Qui ước là những số nào sau dấu phẩy thì làm tròn lên , còn dưới dấu phẩy thì làm tròn xuống 0. Trong trường hợp kia thì nó làm tròn đến số thập phân thứ 3, tức là:
0.6666 = 0.667
0.3333 = 0.3330 = 0.333.

À a viết thiếu. Qui ước sau dấu phẩy mà >=5 thì làm tròn lên. Còn <5 thì làm tròn xuống :))

với lại viết 1/3 là ko đúng. 1.0/3 mới đúng. 1/3 thì nó lấy int/int ra kết quả là int là 0. Viết là 1.0/3 thì nó lấy double/int ra double.

cái này là anh ví dụ ở hệ thập phân thôi. Còn máy tính nó lưu ở hệ nhị phân. Cách biểu diễn khác nhau nhưng cùng giá trị.
viết 1/3 ở hệ thập phân là 0.3333… hay viết là 0.(3) số nằm trong dấu () lặp lại vô hạn.
ở hệ nhị phân thì
với số nguyên, ví dụ 11, được biểu diễn là 10112 (8+2+1 = 23 + 21 + 20 = 11). Số 2 nhỏ ký hiệu cho 1011 là biểu diễn ở dạng nhị phân.
với số thập phân, ví dụ 1.625, được biểu diễn là 1.1012 (1 + 0.5 + 0.125 = 20 + 2-1 + 2-3 = 1.625)
vậy với 1/3 ở hệ nhị phân được biểu diễn là 0.010101010101…2 hay viết là 0.(01)2
số này sẽ được viết lại dưới dạng khoa học: 1/3 = 1.(01)2 x 2-2

với kiểu double, máy tính chỉ lưu được 52 bit sau dấu phẩy, nên phần còn thừa là 01010101… Vì có số 0 đầu tiên nên 1.0/3 sẽ đơn giản bỏ đi phần thừa này. Như vậy 1.0/3 kiểu double thực tế bé hơn 1/3
std::cout << std::setprecision(20) << 1.0/3 << "\n";
sẽ cho ra
0.33333333333333331483

với kiểu float, máy tính lưu được ít hơn: 23 bit sau dấu phẩy. Như vậy phần thừa là 101010101… Vì phần thừa có số 1 đầu tiên nên 1.0f/3 sẽ cộng thêm 1 bit vào nữa, nên 23 bit được lưu là 0101…01011 thay vì 0101…01010. Như vậy 1.0f/3 sẽ lớn hơn 1/3.
std::cout << std::setprecision(10) << 1.0f/3 << "\n";
sẽ cho ra
0.3333333433

1/3 với 2/3 có lẽ gần nhau quá nên máy tính tính ra đúng. Thử với 1/6 và 5/6 sẽ thấy:

double f  = 1.0/6;
double f5 = 5.0/6;
std::cout << std::setprecision(20) << f << "\n";
std::cout << std::setprecision(20) << f+f+f+f+f << "\n";
std::cout << std::setprecision(20) << f5 << "\n";

kết quả

0.16666666666666665741
0.83333333333333325932
0.83333333333333337034

f = 1.0/6 bé hơn 1/6 (1.0/6 = 1.(01)2 x 2-3 phần dư 0101… bỏ qua nên bé hơn)
f cộng 5 lần sẽ ra kết quả bé hơn 5/6
f5 = 5.0/6 lớn hơn 5/6 (5.0/6 = 1.1(01)2 x 2-1 phần dư 1010… cộng thêm 1 nên lớn hơn

9 Likes
83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?