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ề baseexponent. Cả baseexponent thường là kiểu double, kết quả cũng là double.
  • sqrt(x): Tính căn bậc hai. Trả về căn bậc hai của x. 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ủa x. 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óc x (radian).
  • cos(x): Tính cosin của góc x (radian).
  • tan(x): Tính tang của góc x (radian).
  • asin(x): Tính arc sine (sin ngược) của x. Trả về góc (radian) mà sin của nó bằng x. Giá trị x phải trong khoảng [-1, 1].
  • acos(x): Tính arc cosine (cosin ngược) của x. Trả về góc (radian) mà cosin của nó bằng x. Giá trị x phải trong khoảng [-1, 1].
  • atan(x): Tính arc tangent (tang ngược) của x. Trả về góc (radian) mà tang của nó bằng x.
  • atan2(y, x): Tính arc tangent của y/x, sử dụng dấu của cả yx để 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ủa x. x phải dương.
  • log10(x): Tính logarit cơ số 10 của x. x phải dương.
  • log2(x): Tính logarit cơ số 2 của x. 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ơn x.
  • floor(x): Làm tròn xuống. Trả về số nguyên lớn nhất không lớn hơn x.
  • 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ủa x. 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 động ab.
  • fmax(a, b): Trả về giá trị lớn hơn giữa hai số dấu phẩy động ab.

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 minmax 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 fminfmax 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 chia x cho y. Kết quả có cùng dấu với x. 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:

  1. Đọ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.
  2. 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ính a 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ện iostream.
      • Kết hợp các phần tử (biến a, chuỗi " x ", biến i, chuỗi " = ", kết quả a * i) với toán tử <<.
      • Đảm bảo in ký tự xuống dòng ('\n' hoặc endl) sau mỗi dòng để mỗi phép tính nằm trên một dòng riêng biệt.
  3. 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.

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
}

Làm thêm nhiều bài tập miễn phí tại đây

Comments

There are no comments at the moment.