Bài 1.4: Hàm toán học trong thư viện cmath trong C++

Bài 1.4: Hàm toán học trong thư viện cmath trong C++
Chào mừng bạn trở lại với chuỗi bài viết về C++ của FullhouseDev! Sau khi đã làm quen với việc nhập xuất cơ bản, chúng ta sẽ bước chân vào một lĩnh vực cực kỳ quan trọng trong lập trình: toán học. Rất nhiều ứng dụng, từ game, đồ họa, phân tích dữ liệu cho đến các bài toán khoa học phức tạp, đều cần đến khả năng tính toán toán học hiệu quả.
May mắn thay, C++ cung cấp cho chúng ta một công cụ mạnh mẽ để xử lý các phép tính này: thư viện cmath
. Thư viện này (kế thừa từ <math.h>
của C nhưng được điều chỉnh để phù hợp với C++) chứa một bộ sưu tập đồ sộ các hàm toán học, từ cơ bản đến nâng cao, giúp chúng ta thực hiện các phép tính như căn bậc hai, lũy thừa, lượng giác, logarit, làm tròn số và nhiều hơn nữa.
Trong bài viết này, chúng ta sẽ cùng nhau khám phá những hàm toán học thông dụng nhất trong thư viện cmath
và cách sử dụng chúng qua các ví dụ minh họa cụ thể. Hãy cùng bắt đầu nhé!
1. Cách sử dụng thư viện cmath
Để có thể sử dụng các hàm toán học trong thư viện cmath
, điều đầu tiên và bắt buộc là bạn phải include header file này vào đầu chương trình của mình:
#include <cmath>
Khi sử dụng <cmath>
, các hàm sẽ nằm trong namespace std
. Vì vậy, bạn sẽ cần dùng tiền tố std::
trước tên hàm (ví dụ: sqrt
, pow
), hoặc sử dụng using namespace std;
.
2. Các Hàm Toán học Thông dụng
Thư viện cmath
cung cấp rất nhiều hàm. Dưới đây là một số hàm phổ biến và thường được sử dụng nhất:
2.1. Hàm Lũy thừa và Căn
pow(base, exponent)
: Tính lũy thừa. Trả vềbase
mũexponent
. Cảbase
vàexponent
thường là kiểudouble
, kết quả cũng làdouble
.sqrt(x)
: Tính căn bậc hai. Trả về căn bậc hai củax
.x
phải không âm. Trả vềdouble
.cbrt(x)
: Tính căn bậc ba. Trả về căn bậc ba củax
. Hoạt động với cả số dương và âm. Trả vềdouble
. (Có từ C++11)
Hãy xem ví dụ:
#include <iostream>
#include <cmath>
int main() {
double base = 2.0;
double exponent = 3.0;
// Tính 2 mũ 3
double result_pow = pow(base, exponent);
cout << "pow(" << base << ", " << exponent << ") = " << result_pow << endl; // Output: 8
double number_sqrt = 9.0;
// Tính căn bậc hai của 9
double result_sqrt = sqrt(number_sqrt);
cout << "sqrt(" << number_sqrt << ") = " << result_sqrt << endl; // Output: 3
double number_cbrt = 27.0;
// Tính căn bậc ba của 27
double result_cbrt = cbrt(number_cbrt);
cout << "cbrt(" << number_cbrt << ") = " << result_cbrt << endl; // Output: 3
double number_cbrt_neg = -8.0;
// Tính căn bậc ba của -8
double result_cbrt_neg = cbrt(number_cbrt_neg);
cout << "cbrt(" << number_cbrt_neg << ") = " << result_cbrt_neg << endl; // Output: -2
return 0;
}
Trong ví dụ trên, chúng ta thấy cách sử dụng pow
để tính lũy thừa (2^3 = 8), sqrt
để tính căn bậc hai (sqrt(9) = 3), và cbrt
để tính căn bậc ba cho cả số dương (cbrt(27) = 3) và số âm (cbrt(-8) = -2).
2.2. Hàm Lượng giác
Các hàm lượng giác trong cmath
làm việc với radian, không phải độ. Đây là một điểm quan trọng cần lưu ý. Nếu bạn có góc theo độ, bạn cần chuyển đổi sang radian trước khi sử dụng các hàm này (radian = degree * PI / 180
).
sin(x)
: Tính sin của gócx
(radian).cos(x)
: Tính cosin của gócx
(radian).tan(x)
: Tính tang của gócx
(radian).asin(x)
: Tính arc sine (sin ngược) củax
. Trả về góc (radian) mà sin của nó bằngx
. Giá trịx
phải trong khoảng [-1, 1].acos(x)
: Tính arc cosine (cosin ngược) củax
. Trả về góc (radian) mà cosin của nó bằngx
. Giá trịx
phải trong khoảng [-1, 1].atan(x)
: Tính arc tangent (tang ngược) củax
. Trả về góc (radian) mà tang của nó bằngx
.atan2(y, x)
: Tính arc tangent củay/x
, sử dụng dấu của cảy
vàx
để xác định góc đúng trong khoảng [-PI, PI]. Rất hữu ích trong tọa độ cực.
Lưu ý về PI: Thư viện cmath
có thể cung cấp hằng số PI (ví dụ: M_PI
), nhưng việc này không được đảm bảo trên mọi hệ thống theo chuẩn C++. Cách tốt nhất là tự định nghĩa PI với độ chính xác mong muốn hoặc sử dụng các cách lấy PI khác (ví dụ: acos(-1.0)
).
Ví dụ về hàm lượng giác:
#include <iostream>
#include <cmath>
// Định nghĩa PI nếu M_PI không có sẵn (nên làm để đảm bảo tính di động)
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
int main() {
double angle_deg = 90.0;
// Chuyển đổi độ sang radian
double angle_rad = angle_deg * M_PI / 180.0;
cout << "Goc " << angle_deg << " do tuong duong " << angle_rad << " radian." << endl;
// Tính sin, cos
cout << "sin(" << angle_rad << ") = " << sin(angle_rad) << endl; // Gần 1
cout << "cos(" << angle_rad << ") = " << cos(angle_rad) << endl; // Gần 0
double value_sin = 1.0;
// Tính arc sine của 1
double result_asin = asin(value_sin);
cout << "asin(" << value_sin << ") = " << result_asin << " radian (" << result_asin * 180.0 / M_PI << " do)" << endl; // Gần PI/2
double y = 1.0;
double x = 1.0;
// Tính arc tangent của y/x (1/1) sử dụng atan2
double result_atan2 = atan2(y, x);
cout << "atan2(" << y << ", " << x << ") = " << result_atan2 << " radian (" << result_atan2 * 180.0 / M_PI << " do)" << endl; // Gần PI/4 (45 độ)
return 0;
}
Trong code trên, chúng ta thấy cách tính sin và cosin cho góc 90 độ (đã đổi sang radian). Chúng ta cũng sử dụng asin
để tìm góc có sin bằng 1 (là 90 độ hay PI/2 radian) và atan2
để tìm góc cho điểm (1, 1) (là 45 độ hay PI/4 radian), minh họa cách nó khác với atan(y/x)
thông thường bằng cách sử dụng dấu của cả x và y.
2.3. Hàm Mũ và Logarit
exp(x)
: Tính số e (cơ số của logarit tự nhiên, xấp xỉ 2.71828) mũx
.log(x)
: Tính logarit tự nhiên (cơ số e) củax
.x
phải dương.log10(x)
: Tính logarit cơ số 10 củax
.x
phải dương.log2(x)
: Tính logarit cơ số 2 củax
.x
phải dương. (Có từ C++11)
Ví dụ:
#include <iostream>
#include <cmath>
int main() {
double val_exp = 1.0;
// Tính e mũ 1
cout << "exp(" << val_exp << ") = " << exp(val_exp) << endl; // Gần e (2.71828)
double val_log = exp(1.0); // Sử dụng giá trị của e mũ 1
// Tính logarit tự nhiên của e mũ 1
cout << "log(" << val_log << ") = " << log(val_log) << endl; // Gần 1
double val_log10 = 100.0;
// Tính logarit cơ số 10 của 100
cout << "log10(" << val_log10 << ") = " << log10(val_log10) << endl; // Output: 2
#ifdef __cplusplus // log2 có từ C++11
double val_log2 = 8.0;
// Tính logarit cơ số 2 của 8
cout << "log2(" << val_log2 << ") = " << log2(val_log2) << endl; // Output: 3
#endif
return 0;
}
Code này minh họa cách sử dụng exp
để tính e mũ 1, log
để tính logarit tự nhiên của giá trị đó (kết quả gần 1), log10
để tính logarit cơ số 10 của 100 (kết quả 2), và log2
để tính logarit cơ số 2 của 8 (kết quả 3, kiểm tra sự hỗ trợ của C++11).
2.4. Hàm Làm tròn
Thư viện cmath
cung cấp nhiều cách làm tròn số thập phân:
ceil(x)
: Làm tròn lên. Trả về số nguyên nhỏ nhất không nhỏ hơnx
.floor(x)
: Làm tròn xuống. Trả về số nguyên lớn nhất không lớn hơnx
.round(x)
: Làm tròn đến số nguyên gần nhất. Nếu phần thập phân là 0.5, nó làm tròn lên (ra xa số 0). (Có từ C++11)trunc(x)
: Cắt bỏ phần thập phân. Làm tròn về phía số 0. (Có từ C++11)
Ví dụ:
#include <iostream>
#include <cmath>
int main() {
double val_pos = 5.7;
double val_neg = -5.7;
// Làm tròn lên
cout << "ceil(" << val_pos << ") = " << ceil(val_pos) << endl; // Output: 6
cout << "ceil(" << val_neg << ") = " << ceil(val_neg) << endl; // Output: -5
// Làm tròn xuống
cout << "floor(" << val_pos << ") = " << floor(val_pos) << endl; // Output: 5
cout << "floor(" << val_neg << ") = " << floor(val_neg) << endl; // Output: -6
#ifdef __cplusplus // round, trunc có từ C++11
double val_round1 = 5.5;
double val_round2 = -5.5;
// Làm tròn gần nhất (0.5 làm tròn ra xa 0)
cout << "round(" << val_round1 << ") = " << round(val_round1) << endl; // Output: 6
cout << "round(" << val_round2 << ") = " << round(val_round2) << endl; // Output: -6
// Cắt bỏ phần thập phân (làm tròn về 0)
cout << "trunc(" << val_pos << ") = " << trunc(val_pos) << endl; // Output: 5
cout << "trunc(" << val_neg << ") = " << trunc(val_neg) << endl; // Output: -5
#endif
return 0;
}
Ví dụ này làm rõ sự khác biệt giữa các phương pháp làm tròn, đặc biệt là với số âm. ceil
luôn đi lên (đến số nguyên lớn hơn hoặc bằng), floor
luôn đi xuống (đến số nguyên nhỏ hơn hoặc bằng). round
làm tròn theo quy tắc thông thường, còn trunc
chỉ đơn giản là bỏ đi phần thập phân (làm tròn về phía 0).
2.5. Hàm Giá trị Tuyệt đối
abs(x)
: Tính giá trị tuyệt đối củax
. Hàm này được overload cho các kiểu dữ liệu khác nhau (int, long, float, double, long double).fabs(x)
: Tính giá trị tuyệt đối của số dấu phẩy động (float
,double
,long double
).abs
là cách dùng hiện đại và thường được ưu tiên.
Ví dụ:
#include <iostream>
#include <cmath> // Cho abs của số thực và fabs
#include <cstdlib> // Theo truyền thống cho abs của số nguyên
int main() {
int int_val = -10;
double double_val = -10.5;
float float_val = -10.5f;
// Sử dụng abs (overload cho các kiểu)
cout << "abs(" << int_val << ") = " << abs(int_val) << endl; // Output: 10
cout << "abs(" << double_val << ") = " << abs(double_val) << endl; // Output: 10.5
// Sử dụng fabs (chỉ cho số thực)
cout << "fabs(" << float_val << ") = " << fabs(float_val) << endl; // Output: 10.5
return 0;
}
Code trên minh họa việc sử dụng abs
cho cả số nguyên và số thực (do nó được overload). fabs
cũng thực hiện cùng chức năng cho số thực.
2.6. Hàm Min/Max cho số thực
fmin(a, b)
: Trả về giá trị nhỏ hơn giữa hai số dấu phẩy độnga
vàb
.fmax(a, b)
: Trả về giá trị lớn hơn giữa hai số dấu phẩy độnga
vàb
.
Lưu ý: Các hàm này chuyên biệt cho kiểu dấu phẩy động. Đối với các kiểu dữ liệu khác hoặc muốn so sánh nhiều giá trị, bạn có thể sử dụng min
và max
trong thư viện <algorithm>
.
Ví dụ:
#include <iostream>
#include <cmath> // cho fmin/fmax
#include <algorithm> // cho min/max (minh họa)
int main() {
double a = 12.3;
double b = 4.5;
// Tìm giá trị nhỏ hơn/lớn hơn giữa hai số thực
cout << "fmin(" << a << ", " << b << ") = " << fmin(a, b) << endl; // Output: 4.5
cout << "fmax(" << a << ", " << b << ") = " << fmax(a, b) << endl; // Output: 12.3
int x = 10;
int y = 20;
// Sử dụng min/max cho số nguyên (từ <algorithm>)
cout << "min(" << x << ", " << y << ") = " << min(x, y) << endl; // Output: 10
return 0;
}
Ví dụ này cho thấy cách fmin
và fmax
hoạt động với số thực. Phần code thêm về min
từ <algorithm>
chỉ mang tính minh họa sự khác biệt giữa các thư viện cho mục đích so sánh min/max.
2.7. Hàm Số dư (Floating-point Remainder)
fmod(x, y)
: Tính số dư khi chiax
choy
. Kết quả có cùng dấu vớix
. Lưu ý: Đây không phải là phép toán modulo (%) truyền thống, mà là số dư sau phép chia với phần thập phân.
Ví dụ:
#include <iostream>
#include <cmath>
int main() {
double num1 = 10.5;
double den1 = 3.0;
// 10.5 = 3 * 3 + 1.5 -> số dư 1.5
double remainder1 = fmod(num1, den1);
cout << "fmod(" << num1 << ", " << den1 << ") = " << remainder1 << endl; // Output: 1.5
double num2 = -10.5;
double den2 = 3.0;
// -10.5 = 3 * (-3) + (-1.5) -> số dư -1.5
double remainder2 = fmod(num2, den2);
cout << "fmod(" << num2 << ", " << den2 << ") = " << remainder2 << endl; // Output: -1.5
return 0;
}
Code trên cho thấy fmod(10.5, 3.0)
trả về 1.5, vì 10.5 = 3 * 3 + 1.5. Với số âm fmod(-10.5, 3.0)
, kết quả là -1.5, có cùng dấu với số bị chia (-10.5).
3. Lưu ý quan trọng: Độ chính xác của số thực
Khi làm việc với các hàm trong cmath
, bạn cần luôn nhớ rằng các phép tính với số dấu phẩy động (float
, double
) không phải lúc nào cũng chính xác tuyệt đối do cách biểu diễn số trong máy tính. Đôi khi, kết quả có thể hơi khác so với giá trị lý thuyết do sai số làm tròn.
Điều này có nghĩa là bạn nên tránh so sánh trực tiếp hai số thực bằng toán tử ==
. Thay vào đó, hãy kiểm tra xem hiệu của chúng có đủ nhỏ (bên trong một ngưỡng sai số epsilon) hay không: abs(a - b) < epsilon
.
Bài tập ví dụ: C++ Bài 1.A4: Bảng cửu chương
Viết chương tình tính in ra bảng cửu chương của số nguyên \(a\).
INPUT FORMAT
Dòng đầu tiên chứ giá trị của số nguyên \(a(1 \leq a \leq 9)\).
OUTPUT FORMAT
In ra bảng cửu chương tương ứng của số nguyên \(a\).
Ví dụ 1:
Input
5
Ouput
5 x 1 = 5
5 x 2 = 10
5 x 3 = 15
5 x 4 = 20
5 x 5 = 25
5 x 6 = 30
5 x 7 = 35
5 x 8 = 40
5 x 9 = 45
5 x 10 = 50
Giải thích ví dụ mẫu:
- Bài toán yêu cầu in bảng cửu chương của số 5, kết quả là từ 5 x 1 đến 5 x 10.
<br>
Lời giải bài tập này: Tại đây
Group giải đáp thắc mắc: Lập trình 24h
Fanpage CLB: CLB lập trình Full House- Việt Nam
Youtube: CLB Lập Trình Full House Chào bạn, đây là hướng dẫn giải bài tập in bảng cửu chương bằng C++ theo yêu cầu của bạn:
Bài toán này khá đơn giản, bạn cần thực hiện các bước sau:
Đọc dữ liệu đầu vào:
- Bạn cần khai báo một biến kiểu số nguyên để lưu giá trị
a
mà người dùng nhập vào. - Sử dụng thư viện
iostream
chuẩn của C++ (#include <iostream>
) để thực hiện việc đọc. - Dùng đối tượng
cin
để đọc giá trị từ bàn phím và lưu vào biến đã khai báo.
- Bạn cần khai báo một biến kiểu số nguyên để lưu giá trị
In bảng cửu chương:
- Bảng cửu chương của số
a
bao gồm các phép tínha x 1
,a x 2
, ...,a x 10
. - Bạn cần lặp lại một hành động (tính toán và in một dòng) 10 lần, với số nhân tăng dần từ 1 đến 10.
- Cấu trúc lặp
for
là lựa chọn phù hợp nhất cho trường hợp này, vì bạn biết rõ số lần lặp (từ 1 đến 10). - Bên trong vòng lặp, ở mỗi bước lặp (ví dụ, với biến đếm là
i
), bạn cần:- Tính kết quả của phép nhân:
a * i
. - In ra màn hình dòng theo định dạng "a x i = kết quả".
- Sử dụng đối tượng
cout
từ thư việniostream
. - Kết hợp các phần tử (biến
a
, chuỗi " x ", biếni
, chuỗi " = ", kết quảa * i
) với toán tử<<
. - Đảm bảo in ký tự xuống dòng (
'\n'
hoặcendl
) sau mỗi dòng để mỗi phép tính nằm trên một dòng riêng biệt.
- Tính kết quả của phép nhân:
- Bảng cửu chương của số
Kết thúc chương trình:
- Hàm
main
nơi bạn viết code chính nên trả về giá trị 0 để báo hiệu chương trình chạy thành công.
- Hàm
Gợi ý cấu trúc code:
// Bao gồm thư viện cần thiết
// Hàm main là nơi chương trình bắt đầu
int main() {
// Khai báo biến cho số a
// Đọc giá trị của a từ cin
// Bắt đầu vòng lặp for từ 1 đến 10 (inclusive)
// for (khởi tạo biến đếm i; điều kiện lặp; cập nhật biến đếm) {
// In ra màn hình dòng "a x i = a*i" sử dụng cout
// Đừng quên xuống dòng sau mỗi lần in
// }
// Trả về 0
}
Comments