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 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ềbasemũexponent. Cảbasevàexponentthườ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.xphả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() {
using namespace std;
double b = 2.0, e = 3.0;
cout << "pow(" << b << ", " << e << ") = " << pow(b, e) << endl;
double sc = 9.0;
cout << "sqrt(" << sc << ") = " << sqrt(sc) << endl;
double sc3 = 27.0;
cout << "cbrt(" << sc3 << ") = " << cbrt(sc3) << endl;
double sc3a = -8.0;
cout << "cbrt(" << sc3a << ") = " << cbrt(sc3a) << endl;
return 0;
}
Output:
pow(2, 3) = 8
sqrt(9) = 3
cbrt(27) = 3
cbrt(-8) = -2
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ịxphả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ịxphả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ảyvà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>
int main() {
using namespace std;
const double PI = acos(-1.0);
double gd = 90.0;
double gr = gd * PI / 180.0;
cout << "Goc " << gd << " do tuong duong " << gr << " radian." << endl;
cout << "sin(" << gr << ") = " << sin(gr) << endl;
cout << "cos(" << gr << ") = " << cos(gr) << endl;
double s = 1.0;
double rs = asin(s);
cout << "asin(" << s << ") = " << rs << " radian (" << rs * 180.0 / PI << " do)" << endl;
double y = 1.0, x = 1.0;
double ra = atan2(y, x);
cout << "atan2(" << y << ", " << x << ") = " << ra << " radian (" << ra * 180.0 / PI << " do)" << endl;
return 0;
}
Output:
Goc 90 do tuong duong 1.5708 radian.
sin(1.5708) = 1
cos(1.5708) = 6.12323e-17
asin(1) = 1.5708 radian (90 do)
atan2(1, 1) = 0.785398 radian (45 do)
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.xphải dương.log10(x): Tính logarit cơ số 10 củax.xphải dương.log2(x): Tính logarit cơ số 2 củax.xphải dương. (Có từ C++11)
Ví dụ:
#include <iostream>
#include <cmath>
int main() {
using namespace std;
double e = 1.0;
cout << "exp(" << e << ") = " << exp(e) << endl;
double l = exp(1.0);
cout << "log(" << l << ") = " << log(l) << endl;
double l10 = 100.0;
cout << "log10(" << l10 << ") = " << log10(l10) << endl;
double l2 = 8.0;
cout << "log2(" << l2 << ") = " << log2(l2) << endl;
return 0;
}
Output:
exp(1) = 2.71828
log(2.71828) = 1
log10(100) = 2
log2(8) = 3
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() {
using namespace std;
double vp = 5.7, vn = -5.7;
cout << "ceil(" << vp << ") = " << ceil(vp) << endl;
cout << "ceil(" << vn << ") = " << ceil(vn) << endl;
cout << "floor(" << vp << ") = " << floor(vp) << endl;
cout << "floor(" << vn << ") = " << floor(vn) << endl;
double r1 = 5.5, r2 = -5.5;
cout << "round(" << r1 << ") = " << round(r1) << endl;
cout << "round(" << r2 << ") = " << round(r2) << endl;
cout << "trunc(" << vp << ") = " << trunc(vp) << endl;
cout << "trunc(" << vn << ") = " << trunc(vn) << endl;
return 0;
}
Output:
ceil(5.7) = 6
ceil(-5.7) = -5
floor(5.7) = 5
floor(-5.7) = -6
round(5.5) = 6
round(-5.5) = -6
trunc(5.7) = 5
trunc(-5.7) = -5
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).abslà cách dùng hiện đại và thường được ưu tiên.
Ví dụ:
#include <iostream>
#include <cmath>
int main() {
using namespace std;
int i = -10;
double d = -10.5;
float f = -10.5f;
cout << "abs(" << i << ") = " << abs(i) << endl;
cout << "abs(" << d << ") = " << abs(d) << endl;
cout << "fabs(" << f << ") = " << fabs(f) << endl;
return 0;
}
Output:
abs(-10) = 10
abs(-10.5) = 10.5
fabs(-10.5) = 10.5
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 độngavàb.fmax(a, b): Trả về giá trị lớn hơn giữa hai số dấu phẩy độngavà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>
#include <algorithm>
int main() {
using namespace std;
double a = 12.3, b = 4.5;
cout << "fmin(" << a << ", " << b << ") = " << fmin(a, b) << endl;
cout << "fmax(" << a << ", " << b << ") = " << fmax(a, b) << endl;
int x = 10, y = 20;
cout << "min(" << x << ", " << y << ") = " << min(x, y) << endl;
return 0;
}
Output:
fmin(12.3, 4.5) = 4.5
fmax(12.3, 4.5) = 12.3
min(10, 20) = 10
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 chiaxchoy. 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() {
using namespace std;
double n1 = 10.5, d1 = 3.0;
cout << "fmod(" << n1 << ", " << d1 << ") = " << fmod(n1, d1) << endl;
double n2 = -10.5, d2 = 3.0;
cout << "fmod(" << n2 << ", " << d2 << ") = " << fmod(n2, d2) << endl;
return 0;
}
Output:
fmod(10.5, 3) = 1.5
fmod(-10.5, 3) = -1.5
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.
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ị
amà người dùng nhập vào. - Sử dụng thư viện
iostreamchuẩ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ố
abao 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
forlà 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
couttừ 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
mainnơ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:
#include <iostream>
int main() {
using namespace std;
int a;
cin >> a;
for (int i = 1; i <= 10; ++i) {
cout << a << " x " << i << " = " << a * i << endl;
}
return 0;
}
Output cho Input 5:
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
Comments