30/09/2018, 19:44
Thắc mắc về kiểu float trong C/C++
void ChuyenDoi(double so, int k){
char chuso[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
FILE *f;
int a;
char mang1[50],mang2[20];
int i = 0,j = 0;
double b;
//lay phan nguyen
a = floor(so);
//lay phan thap phan
b = so - a;
//chuyen doi phan nguyen
while(a > 0)
{
mang1[i] = chuso[a % k];
a = a / k;
i++;
}
//chuyen doi phan thap phan
while(j!=20 )
{
int tam;
b = b * (float)k;
tam = floor(b);
mang2[j] = chuso[tam];
b = b - floor(b);
cout<<b<<"--";
j++;
}
//
//in ket qua ra man hinh
cout<<"Ket Qua Chuyen So "<<so<<" Sang He Co So "<<k<<" La : ";
for(int m = i-1;m>=0;m--)
{
cout<<mang1[m];
}
cout<<".";
for(int m = 0;m<j;m++)
{
cout<<mang2[m];
}
//ghi ket qua vao file
f=fopen("ketqua.txt","wt");
fprintf(f,"%s","Ket Qua Chuyen So ");
fprintf(f,"%f",so);
fprintf(f,"%s"," Sang He Co So ");
fprintf(f,"%d",k);
fprintf(f,"%s"," La : ");
for(int m = i-1;m>=0;m--)
fprintf(f,"%c",mang1[m]);
fprintf(f,"%c",'.');
for(int m = 0;m<j;m++)
fprintf(f,"%c",mang2[m]);
fclose(f);
}
main()
{
double so;
int coso;
do{
cout<<"Nhap vao so can chuyen doi : ";
cin>>so;
}while(so < 0);
do{
cout<<"Nhap vao he co so can chuyen doi(2 - 16) : ";
cin>>coso;
}while(coso < 2 || coso > 16);
ChuyenDoi(so,coso);
getch();
}
Mình không hiểu tại sao đoạn code này b = b - floor(b); khi trừ nó lại cho ra số lẻ phía cuối không mong muốn mình ví dụ 0.4400001( Kết quả tính máy tính casio là 0.44 nhưng chạy trên thuật toán này lại ra là 0.4400001). Bởi vì có số lẻ như vậy đã làm cho thuật toán trên của mình cũng chạy sai. Có cách nào khắc phục vấn đề như trên không các bạn.
Bài liên quan
Bạn nhập số 0.44 vào bằng lệnh cin hay sao?
mình không nhập bằng lệnh cin. Cái trên chỉ là ví dụ thôi.
Vậy bạn có thể nói rõ hơn khi nào bạn bị trường hợp như trên không
Thuật toán của mình là đổi một số thực sang hệ cơ số 2- 16
Bây giờ mình sẽ chạy thuật toán chuyển từ số 3.14 sang hệ 16
Đầu tiên là thuật toán sẽ lấy phần nguyên và phần lẻ của số 3.14 tức là thành số 3 và 0.14. Bây giờ việc của mình là đổi số 3 và 0.14 thành hệ cơ số 16. Việc chuyển số 3 thì chuyển bình thường mình không nói. Còn chuyển số 0.14 thì ta nhấn liên tiếp cho 16 sau đó lấy phần lẻ nhân tiếp cho 16 cứ tiếp tục như vậy ta lấy phần nguyên sẽ được cơ số 16 của 0.14. Thuật toán chạy như sau:
0.14 * 16 = 2.24 -> 2 lẻ 0.24
0.24 * 16 = 3.84 -> 3 lẻ 0.84
0.84 * 16 = 13.44 - > 13 (D) lẻ 0.44
0.44 * 16 = 7.04 -> 7 lẻ 0.04
0.04 * 16 =0.64 ->0 lẻ 0.64
0.67 * 16 = 10.24 - > 10 (A) lẻ 0.24
0.24 * 16 = 3.84 -> 3 lẻ 0.84
0.84 * 16 = 13.44 ->13 lẻ 0.440001(sai) đáng lẻ phải là 0.44 mình không hiểu sao thuật toán nó lại chạy ra 0.440001
cứ tiếp tục như vậy bắt đầu nó xuất hiện sô lẻ không mong muốn và từ đây thuật toán sai hết
hoặc bạn có thể chạy thử đoạn code này bạn sẽ nhận ra vấn đề của mình
máy tính ko thể lưu chính xác số thực được.
ví dụ ở hệ thập phân thì ko thể nào biểu diễn chính xác 1/3 hay 1/7 được. Tương tự, máy tính lưu số thực dưới dạng số nhị phân, mà trong hệ nhị phân thì ko tài nào biểu diễn chính xác1/5 được. Vì vậy số 0.14 = 14/100 = 7/50 máy tính ko thể lưu chính xác. Qua nhiều phép tính thì nó sẽ lòi ra phần sai số.
nếu bạn muốn chính xác thì xài int. 0.14 chuyển thành 14, lấy “số lẻ” bằng phép chia lấy số dư:
b %= 100;
14 * 16 = 224 -> 2 lẻ 24
24 * 16 = 384 -> 3 lẻ 84
84 * 16 = 1344 - > 13 (D) lẻ 44
44 * 16 = 704 -> 7 lẻ 4
v.v…
Mình hiểu vấn đề của bạn rồi. Nhưng đó là hạn chế của kiểu float, ví dụ như số 0.1 thập phân, có biểu diễn nhị phân là 0.0001100110011, lưu bằng kiểu float theo chuẩn IEEE 754 là số 0.10000000149, nên nhân chia cộng trừ một hồi sẽ không chính xác nữa. Bạn có thể dùng tip này: khi nhập thì lưu số thực lại dạng chuỗi ký tự để dễ tách riêng phần phân và phần nguyên. Nhân phần phân lên 10^n (n là chiều dài phần phân) rồi chuyển đổi.