Các kiểu thực (double, float)
Kiểu số thực (hay kiểu số dấu phẩy động) biểu diễn các giá trị với một phần thập phân. Trong ngôn ngữ MQL5 có hai kiểu số dấu phẩy động. Phương pháp biểu diễn số thực trong bộ nhớ máy tính được định nghĩa bởi chuẩn IEEE 754
và không phụ thuộc vào nền tảng, hệ điều hành hoặc ngôn ngữ lập trình.
Kiểu | Kích thước tính bằng byte | Giá trị dương tối thiểu | Giá trị tối đa | Kiểu trong C++ |
---|---|---|---|---|
float | 4 | 1.175494351e-38 | 3.402823466e+38 | float |
double | 8 | 2.2250738585072014e-308 | 1.7976931348623158e+308 | double |
double
- Kiểu số thực
double
chiếm64 bit
(1 bit dấu, 11 bit mũ và 52 bit mantissa).
float
- Kiểu số thực
float
chiếm 32 bit (1 bit dấu, 8 bit mũ và 23 bit mantissa).
vector
- Mảng một chiều của các số kiểu
double
. - Bộ nhớ cho dữ liệu được phân bổ động.
- Thuộc tính vector có thể được lấy bằng các phương thức, trong khi kích thước vector có thể được thay đổi.
- Mục
vector<double>
có thể được sử dụng trong các hàm mẫu (templage functions).
vectorf
- Mảng một chiều của số kiểu
float
có thể được sử dụng thay cho vector nếu việc mất độ chính xác không quan trọng. - Có thể sử dụng mục
vector<float>
trong các hàm mẫu.
vectorc
- Mảng một chiều của các số kiểu phức tạp (complex) được dùng để xử lý các số phức tạp.
- Mục
vector<complex>
có thể được sử dụng trong các hàm mẫu. - Các phép toán trên các vector kiểu
vectorc
vẫn chưa được triển khai.
matrix
- Ma trận (matrix) là một mảng hai chiều của các số kiểu
double
. - Bộ nhớ cho các phần tử ma trận được phân phối động.
- Thuộc tính ma trận có thể được lấy bằng cách sử dụng các phương thức, trong khi hình dạng ma trận có thể được thay đổi.
- Mục
matrix<double>
có thể được sử dụng trong các hàm mẫu.
matrixf
- Mảng hai chiều của số kiểu float có thể được sử dụng thay cho ma trận nếu việc mất độ chính xác không quan trọng.
- Mục
matrix<float>
có thể được sử dụng trong các hàm mẫu.
matrixc
- Mảng hai chiều của các số kiểu phức tạp được dùng để xử lý các số phức tạp.
- Mục
matrix<complex>
có thể được sử dụng trong các hàm mẫu. - Các phép toán trên các ma trận kiểu matrixc vẫn chưa được triển khai.
Tên
double
có nghĩa là độ chính xác của những con số này gấp đôi độ chính xác của những con số kiểufloat
. Trong hầu hết các trường hợp, kiểudouble
là kiểu thuận tiện nhất. Trong nhiều trường hợp, độ chính xác hạn chế của những con số kiểufloat
là không đủ. Lý do tại sao kiểufloat
vẫn được sử dụng là để tiết kiệm bộ nhớ (điều này quan trọng đối với các mảng số thực lớn).
Hằng số dấu phẩy động bao gồm phần nguyên, phần dấu chấm (.) và phần phân số. Phần nguyên và phần phân số là chuỗi chữ số thập phân.
double c1=1.12123515e-25;
double c2=0.000000000000000000000000112123515; // 24 số 0 sau dấu .
Print("1. c1 =",DoubleToString(c1,16));
// Kết quả: 1. c1 = 0.0000000000000000
Print("2. c1 =",DoubleToString(c1,-16));
// Kết quả: 2. c1 = 1.1212351499999999e-025
Cần nhớ rằng các số thực nói riêng, số thập phân nói chung được lưu trữ trong bộ nhớ với một số độ chính xác hạn chế trong hệ nhị phân. Đó là lý do tại sao nhiều số được biểu diễn chính xác trong hệ thập phân chỉ có thể được viết dưới dạng phân số vô hạn trong hệ nhị phân.
Ví dụ, số 0,3 và 0,7 được biểu diễn trong máy tính dưới dạng phân số vô hạn, trong khi số 0,25 được lưu trữ chính xác vì nó biểu diễn lũy thừa của hai.
Về vấn đề này, chúng tôi đặc biệt khuyến cáo không nên so sánh hai số thực để xem chúng bằng nhau vì việc so sánh như vậy là không chính xác.
VD:
void OnStart()
{
//---
double three=3.0;
double x,y,z;
x=1/three;
y=4/three;
z=5/three;
if(x+y==z)
Print("1/3 + 4/3 == 5/3");
else
Print("1/3 + 4/3 != 5/3");
// Kết quả: 1/3 + 4/3 != 5/3
}
Nếu bạn vẫn cần so sánh đẳng thức của hai số thực thì bạn có thể thực hiện việc này theo hai cách khác nhau. Cách thứ nhất là so sánh sự khác biệt giữa hai số với một đại lượng nhỏ nào đó xác định độ chính xác của phép so sánh.
VD:
bool EqualDoubles(double d1,double d2,double epsilon)
{
if(epsilon<0)
epsilon=-epsilon;
//---
if(d1-d2>epsilon)
return false;
if(d1-d2<-epsilon)
return false;
//---
return true;
}
void OnStart()
{
double d_val=0.7;
float f_val=0.7;
if(EqualDoubles(d_val,f_val,0.000000000000001))
Print(d_val," equals ",f_val);
else
Print("Different: d_val = ",DoubleToString(d_val,16)," f_val = ",DoubleToString(f_val,16));
// Kết quả: Different: d_val= 0.7000000000000000 f_val= 0.6999999880790710
}
Lưu ý rằng giá trị của epsilon
trong ví dụ trên không thể nhỏ hơn hằng số được xác định trước DBL_EPSILON
. Giá trị của hằng số này là 2.2204460492503131e-016
. Hằng số tương ứng với kiểu float
là FLT_EPSILON = 1.192092896e-07
. Ý nghĩa của các giá trị này như sau: đó là giá trị thấp nhất thỏa mãn điều kiện 1.0 + DBL_EPSILON! = 1.0
(đối với các số kiểu float
1.0 + FLT_EPSILON! = 1.0
).
Cách thứ hai cung cấp cách so sánh hiệu chuẩn hóa của hai số thực với số không. Việc so sánh hiệu chuẩn hóa của các số với số không là vô nghĩa, vì bất kỳ phép toán nào với các số chuẩn hóa đều cho kết quả không chuẩn hóa.
VD:
bool CompareDoubles(double number1,double number2)
{
if(NormalizeDouble(number1-number2,8)==0)
return(true);
else
return(false);
}
void OnStart()
{
double d_val=0.3;
float f_val=0.3;
if(CompareDoubles(d_val,f_val))
Print(d_val," equals ",f_val);
else
Print("Different: d_val = ",DoubleToString(d_val,16)," f_val = ",DoubleToString(f_val,16));
// Kết quả: Different: d_val= 0.3000000000000000 f_val= 0.3000000119209290
}
Một số phép toán của bộ đồng xử lý toán học có thể dẫn đến số thực không hợp lệ, không thể sử dụng số này trong các phép toán và phép so sánh vì kết quả của các phép toán với số thực không hợp lệ là không xác định. Ví dụ: khi cố gắng tính arcsine
của 2, kết quả là âm vô cực.
VD:
double abnormal = MathArcsin(2.0);
Print("MathArcsin(2.0) =",abnormal);
// Kết quả: MathArcsin(2.0) = -1.#IND
Bên cạnh âm vô cực còn có cộng vô cực và NaN
(not a number). Để xác định rằng số này không hợp lệ, bạn có thể sử dụng MathIsValidNumber()
. Theo tiêu chuẩn IEEE, họ có một biểu diễn máy đặc biệt. Ví dụ: cộng vô cùng cho loại kép có biểu diễn bit là 0x7FF0 0000 0000 0000
.
VD:
struct str1
{
double d;
};
struct str2
{
long l;
};
//--- Start
str1 s1;
str2 s2;
//---
s1.d=MathArcsin(2.0); // Nhận số không hợp lệ -1.#IND
s2=s1;
printf("1. %f %I64X",s1.d,s2.l);
//---
s2.l=0xFFFF000000000000; // số không hợp lệ -1.#QNAN
s1=s2;
printf("2. %f %I64X",s1.d,s2.l);
//---
s2.l=0x7FF7000000000000; // lớn nhất không phải số SNaN
s1=s2;
printf("3. %f %I64X",s1.d,s2.l);
//---
s2.l=0x7FF8000000000000; // QNaN không phải số nhỏ nhất
s1=s2;
printf("4. %f %I64X",s1.d,s2.l);
//---
s2.l=0x7FFF000000000000; // QNaN không phải số lớn nhất
s1=s2;
printf("5. %f %I64X",s1.d,s2.l);
//---
s2.l=0x7FF0000000000000; // Dương vô cùng 1.#INF và không phải số nhỏ nhất SNaN
s1=s2;
printf("6. %f %I64X",s1.d,s2.l);
//---
s2.l=0xFFF0000000000000; // Âm vô cùng -1.#INF
s1=s2;
printf("7. %f %I64X",s1.d,s2.l);
//---
s2.l=0x8000000000000000; // Âm -0.0
s1=s2;
printf("8. %f %I64X",s1.d,s2.l);
//---
s2.l=0x3FE0000000000000; // 0.5
s1=s2;
printf("9. %f %I64X",s1.d,s2.l);
//---
s2.l=0x3FF0000000000000; // 1.0
s1=s2;
printf("10. %f %I64X",s1.d,s2.l);
//---
s2.l=0x7FEFFFFFFFFFFFFF; // Số chuẩn hóa lớn nhất (MAX_DBL)
s1=s2;
printf("11. %.16e %I64X",s1.d,s2.l);
//---
s2.l=0x0010000000000000; // Số dương chuẩn hóa nhỏ nhất (MIN_DBL)
s1=s2;
printf("12. %.16e %.16I64X",s1.d,s2.l);
//---
s1.d=0.7; // Cho thấy số 0,7 - phân số vô tận
s2=s1;
printf("13. %.16e %.16I64X",s1.d,s2.l);
/*
1. -1.#IND00 FFF8000000000000
2. -1.#QNAN0 FFFF000000000000
3. 1.#SNAN0 7FF7000000000000
4. 1.#QNAN0 7FF8000000000000
5. 1.#QNAN0 7FFF000000000000
6. 1.#INF00 7FF0000000000000
7. -1.#INF00 FFF0000000000000
8. -0.000000 8000000000000000
9. 0.500000 3FE0000000000000
10. 1.000000 3FF0000000000000
11. 1.7976931348623157e+308 7FEFFFFFFFFFFFFF
12. 2.2250738585072014e-308 0010000000000000
13. 6.9999999999999996e-001 3FE6666666666666
*/