Dẫn nhập
Trong bài học trước, bạn đã tìm hiểu về biến trong ngôn ngữ lập trình C++ và nhận thức về hoạt động của biến, cùng với một số kinh nghiệm cơ bản. Tuy nhiên, trong C++ vẫn còn nhiều kiểu dữ liệu khác mà bạn cần biết, và trong bài học hôm nay, chúng ta sẽ tìm hiểu về hai loại kiểu dữ liệu mới: Số nguyên và Số chấm động trong C++.
Tổng quan về kiểu dữ liệu cơ bản trong C++
Trước khi đi vào chi tiết về kiểu dữ liệu cụ thể, hãy cùng nhau tìm hiểu về khái niệm kiểu dữ liệu trong C++. Trong bài học trước về biến trong C++, chúng ta đã biết rằng biến là tên của một vùng bộ nhớ RAM được sử dụng để lưu trữ thông tin. Mỗi biến có thể lưu trữ một loại thông tin (ví dụ: số nguyên, số thực, ký tự, ...) và kiểu dữ liệu của biến quyết định kích thước và đặc tính của thông tin được lưu trữ trong biến đó.
Trong C++, có nhiều loại kiểu dữ liệu cơ bản khác nhau, mỗi loại có kích thước và phạm vi giá trị khác nhau. Dưới đây là bảng tổng quan về các kiểu dữ liệu cơ bản trong C++:
Ví dụ:
#include using namespace std; int main() { cout << "bool:\t\t" << sizeof(bool) << " bytes" << endl; cout << "char:\t\t" << sizeof(char) << " bytes" << endl; cout << "wchar_t:\t" << sizeof(wchar_t) << " bytes" << endl; // C++11, may not be supported by your compiler cout << "char16_t:\t" << sizeof(char16_t) << " bytes" << endl; cout << "char32_t:\t" << sizeof(char32_t) << " bytes" << endl; cout << "short:\t\t" << sizeof(short) << " bytes" << endl; cout << "int:\t\t" << sizeof(int) << " bytes" << endl; cout << "long:\t\t" << sizeof(long) << " bytes" << endl; // C++11, may not be supported by your compiler cout << "long long:\t" << sizeof(long long) << " bytes" << endl; cout << "float:\t\t" << sizeof(float) << " bytes" << endl; cout << "double:\t\t" << sizeof(double) << " bytes" << endl; cout << "long double:\t" << sizeof(long double) << " bytes" << endl; int n; cout << "n variable:\t" << sizeof(n) << " bytes" << endl; return 0; }
Chương trình trên khi chạy trên Windows 7 x64 (Visual Studio 2015) sẽ cho ra kết quả:
Một điều thú vị là toán tử sizeof
là một trong 3 toán tử không phải là ký hiệu trong C++, còn lại là new
và delete
sẽ được giới thiệu trong phần CẤP PHÁT ĐỘNG (Dynamic Memory Allocation).
Kiểu số nguyên (Integer)
Số nguyên đại diện cho các số nguyên dương (1, 2, 3, ...) và số đối (-1, -2, -3, ...) cùng với số 0. Trong C++, có 5 loại số nguyên cơ bản để sử dụng:
Chú ý: Char là một kiểu dữ liệu đặc biệt, nó vừa là kiểu số nguyên, cũng vừa là kiểu ký tự. Chi tiết về tính chất ký tự của char sẽ được giải thích trong phần ký tự (Character). Ở mục này, bạn có thể coi char là kiểu số nguyên thường.
Sự khác nhau giữa các kiểu số nguyên nằm ở kích thước. Kiểu có kích thước lớn sẽ có thể lưu trữ được những số nguyên lớn. Vùng giá trị của một kiểu số nguyên được xác định bởi kích thước và dấu.
Số nguyên có dấu là các số nguyên dương (1, 2, 3, ...) và số đối (-1, -2, -3, ...) cùng với số 0. Có hai cách để khai báo một biến số nguyên có dấu:
// Khai báo không tường minh, thường được sử dụng char c; short s; int n; // Hoặc khai báo tường minh, sử dụng từ khóa signed signed char c; signed short s; signed int n;
Số nguyên không dấu là các số nguyên dương (1, 2, 3, ...) và số 0. Khi chương trình của bạn chỉ cần lưu trữ những số không âm (ví dụ: chiều cao, cân nặng, độ dài, chỉ số danh sách, ...), bạn có thể khai báo số nguyên không dấu bằng cách sử dụng từ khóa unsigned. Ví dụ:
// Sử dụng từ khóa unsigned unsigned char uc; unsigned short us; unsigned int un;
Lưu ý: Một số nguyên không dấu không thể lưu trữ các số âm, nhưng nó có thể lưu trữ số dương lớn hơn gấp 2 lần số nguyên có dấu.
Dưới đây là bảng miền giá trị của các kiểu số nguyên:
Số chấm động (Floating point numbers)
Trong C++, kiểu số chấm động được sử dụng để đại diện cho các số thực (ví dụ: 69.9696, 3.14159, 0.00001, ...) và thường được sử dụng để lưu trữ những số rất lớn hoặc nhỏ. Cấu trúc lưu trữ của số chấm động được thiết kế theo chuẩn số chấm động của IEEE.
Số chấm động không có từ khóa unsigned. Có 3 kiểu số chấm động khác nhau trong C++: float, double, long double.
Chú ý: Một số môi trường lập trình đồng nhất kiểu long double với kiểu double nên kiểu này ít được sử dụng trong lập trình ứng dụng.
Cách để định nghĩa một biến số chấm động:
// Definitions of floating point numbers float fVarName; double dVarName2; long double ldVarName3;
Chú ý: Mặc định, một hằng số dưới dạng số chấm động sẽ là kiểu double. Để có một số thực kiểu float, bạn cần thêm hậu tố 'f'.
Ví dụ:
// Initializations of floating point numbers float fVarName{4.0f}; // 4.0 means floating point (f suffix means float) double dVarName2{4.0}; // 4.0 means floating point (double by default) long double dVarName3{4.0L}; // 4.0 means floating point (L suffix means long double) int nVarName4{4}; // 4 means integer
Ký hiệu khoa học (Scientific notation)
Ký hiệu khoa học là cách biểu thị các số rất lớn hoặc rất nhỏ. Ví dụ: chu kỳ xoay quanh Mặt Trăng của Trái Đất là 152853.5047 giây. Thay vì viết số đó dưới dạng đầy đủ, chúng ta có thể sử dụng ký hiệu khoa học là 1.528535047 × 10^5 giây. Ký hiệu khoa học cũng được sử dụng cho các số khác:
- 24327 = 2.4327 x 10^4
- 7354 = 7.354 x 10^3
- 0.0078 = 7.8 x 10^-3
- 0.00069 = 6.9 x 10^-4
Chú ý: Số mũ là dương nếu dấu thập phân chuyển sang phải và là âm nếu dấu thập phân chuyển sang trái. Trong C++, bạn có thể sử dụng ký hiệu khoa học để gán giá trị cho biến số chấm động. Dùng ký hiệu 'e' hoặc 'E' để thay thế cho 10.
Ví dụ:
// Initializations of floating point numbers double dVarName1{69000.0}; double dVarName2{6.9e4}; // 6.9e4 is equal to 69000.0 double dVarName3{0.00069}; double dVarName4{6.9E-4}; // 6.9e-4 is equal to 0.00069
Độ chính xác của số chấm động (Precision)
Số chấm động bao gồm số hữu hạn và vô hạn. Số vô hạn có phần thập phân có độ dài vô hạn (ví dụ: 1/6 = 0.1666666666..., PI = 3.141592653589793...), nhưng bộ nhớ máy tính và kiểu dữ liệu các số chấm động lại là hữu hạn. Do đó, biến số chấm động chỉ có thể lưu trữ một độ chính xác nhất định và phần sau của số sẽ bị mất.
Trong C++, khi in ra một số chấm động, mặc định là std::cout
chỉ hiển thị 6 chữ số. Những số ngoài phạm vi này sẽ bị cắt bỏ và làm tròn lên nếu số thứ 7 lớn hơn 5 hoặc số đó có thể được chuyển sang ký hiệu khoa học trong một số trường hợp tùy thuộc vào từng trình biên dịch.
Ví dụ:
#include using namespace std; int main() { double d; d = 9.87654321; cout << d << endl; d = 987.654321; cout << d << endl; d = 987654.321; cout << d << endl; d = 9876543.21; cout << d << endl; d = 0.0000987654321; cout << d << endl; d = 1.23456789; cout << d << endl; return 0; }
Chương trình trên khi chạy trên Windows 7 x64 (Visual Studio 2015) sẽ cho ra kết quả:
Mặc dù khi in ra một số chấm động, std::cout
mặc định là 6 chữ số, bạn vẫn có thể thay đổi độ chính xác của nó bằng cách sử dụng hàm std::setprecision()
trong thư viện
.
Ví dụ:
#include #include // for std::setprecision() using namespace std; int main() { cout << std::setprecision(20); // Show 20 digits float f{ 9.66666666666666666666f }; // Initializations cout << f << endl; double d{ 9.66666666666666666666 }; // Initializations cout << d << endl; return 0; }
Kết quả thu được:
Trong chương trình trên, ta đã thay đổi độ chính xác lên đến 20 chữ số thay vì là 6 chữ số như mặc định. Nhưng dù 2 biến float
và double
đều có 20 chữ số, thì độ chính xác của nó vẫn không đến 20 chữ số.
Thông thường, số chấm động kiểu
float
có độ chính xác đơn (single-precision), chính xác đến 7 chữ số.Double
có độ chính xác kép (double-precision), chính xác đến 16 chữ số. Đó là lý do tại sao chương trình trên lại có những số sau dấu phẩy không chính xác sau một số chữ số chính xác.
Độ chính xác của số chấm động không chỉ ảnh hưởng đến phần thập phân, mà nó cũng có thể ảnh hưởng đến phần nguyên của các số có quá nhiều chữ số.
Ví dụ:
#include #include // for std::setprecision() using namespace std; int main() { float f{ 123456789.0f }; cout << std::setprecision(9); // Show 9 digits cout << f << endl; return 0; }
Kết quả thu được:
Trong ví dụ trên, dù chúng ta đã thay đổi độ chính xác lên đến 9 chữ số, nhưng số chấm động float
vẫn không đủ chính xác để hiển thị đúng các chữ số nguyên.