0

Khám Phá Thế Giới Math "Ngầm" Trong JavaScript: Bí Mật Đằng Sau Các Hàm Số Học Nâng Cao Của ES6

Chào anh em Viblo! 👋

Hỏi thật nhé, khi làm việc với đối tượng Math trong JavaScript, anh em thường dùng những hàm nào? Mình cá là 99% câu trả lời sẽ là: Math.floor(), Math.ceil(), Math.round(), Math.abs(), và huyền thoại Math.random().

Cùng lắm khi cần tìm cực trị thì chúng ta nhớ tới Math.max() và Math.min(). Hết vị!

Nhưng nếu anh em mở file định nghĩa TypeScript core ra, anh em sẽ thấy interface Math chứa đầy những hàm nghe rất "học thuật" và lạ lẫm như clz32, imul, hypot, hay một loạt hàm lượng giác hyperbolic (cosh, sinh, tanh). Hồi mới thấy đống này, mình đã tự hỏi: "Ủa, JavaScript sinh ra để làm web, sao lại nhét mấy hàm tính toán phức tạp như làm game engine hay lập trình đồ họa vào đây làm gì?"

Thực tế, đây là các hàm được bổ sung từ phiên bản ES6 (ES2015). Chúng sinh ra không phải để làm cảnh, mà là vũ khí hạng nặng giúp tối ưu hóa hiệu năng, xử lý dữ liệu nhị phân tốc độ cao, hỗ trợ WebAssembly/WebGL, và sửa chữa các lỗi sai số dấu câu động (floating-point) kinh điển.

Hôm nay, từ những lần mò mẫm tối ưu thuật toán, mình sẽ "giải ngố" cho anh em từng hàm con một trong cái interface "nhức não" này theo cách thực dụng nhất nhé!

Nhóm 1: Thao Tác Bit Và Tối Ưu Tầng Thấp (Low-Level Math)

JavaScript mặc định lưu trữ mọi con số dưới dạng 64-bit Floating-Point (Double precision). Tuy nhiên, để làm việc với đồ họa (WebGL), mật mã học (Cryptography) hoặc WebAssembly, chúng ta cần tính toán trên số nguyên 32-bit (32-bit Integer). Nhóm hàm này là cứu cánh cho việc đó.

1. Math.clz32(x) — Đếm số bit 0 ở đầu (Count Leading Zeroes)

Hàm này sẽ chuyển con số của bạn về dạng nhị phân 32-bit, sau đó đếm xem có bao nhiêu chữ số 0 ở phía trước.

console.log(Math.clz32(1));  // 31 (Vì số 1 là: 00000000...00000001 -> có 31 số 0 ở đầu)
console.log(Math.clz32(4));  // 29 (Số 4 là: 00000000...00000100 -> có 29 số 0 ở đầu)
console.log(Math.clz32(0));  // 32
  • Ứng dụng thực chiến: Dùng trong các thuật toán nén dữ liệu, tính toán ma trận bit, hoặc tìm vị trí bit cao nhất một cách chớp nhoáng mà không cần viết vòng lặp chuyển đổi chuỗi nhị phân.

2. Math.imul(x, y) — Nhân số nguyên 32-bit kiểu C-like

Nếu bạn lấy hai số rất lớn nhân với nhau trong JS, bạn sẽ bị mất độ chính xác khi vượt quá giới hạn an toàn (Number.MAX_SAFE_INTEGER). Hàm Math.imul thực hiện phép nhân số nguyên 32-bit giống hệt như ngôn ngữ C hoặc Java, nếu tràn số (overflow), nó sẽ tự động bo tròn (wrap around).

console.log(Math.imul(2000000000, 2)); // -294967296 (Tràn số nguyên 32-bit, ra số âm chuẩn cấu trúc máy tính)
  • Ứng dụng thực chiến: Đây là hàm bắt buộc phải có để tối ưu hóa hiệu năng cho WebAssembly và asm.js. Nó giúp các dự án game bằng C++ biên dịch sang JavaScript chạy với tốc độ gần như native.

3. Math.fround(x) — Ép về số thực đơn độ chính xác (32-bit Float)

JavaScript dùng số thực 64-bit, nhưng WebGL hay các hệ thống đồ họa lại dùng số thực 32-bit (Single precision). Math.fround giúp làm tròn con số về dạng 32-bit Float gần nhất.

console.log(Math.fround(1.337)); // 1.3370000123977661
  • Ứng dụng thực chiến: Giúp kiểm soát và đồng bộ chính xác dữ liệu khi bạn truyền mảng số thực vào các bộ đệm (Float32Array) của WebGL, tránh hiện tượng sai số đổ bóng hoặc răng cưa đồ họa.

Nhóm 2: Tiện Ích Đời Thường (Utility Math) Đây là nhóm hàm cực kỳ hữu dụng mà anh em có thể mang vào các dự án web thông thường ngay ngày mai để thay thế cho đống code if-else rườm rà.

4. Math.sign(x) — Kiểm tra dấu của một số

Hàm này trả về 1 nếu số dương, -1 nếu số âm, 0 nếu là số 0, -0 nếu là số -0, và NaN nếu không phải là số.

console.log(Math.sign(42));    // 1
console.log(Math.sign(-99));   // -1
  • Ứng dụng thực chiến: Thay vì viết if (speed > 0) direction = 1 else ..., bạn chỉ cần direction = Math.sign(speed). Cực gọn!
  1. Math.trunc(x) — Chặt cụt phần thập phân Hàm này đơn giản là vứt bỏ toàn bộ phần dấu phẩy thập phân của một số để lấy phần nguyên, không hề làm tròn.

⚠️ Cạm bẫy phân biệt với Math.floor(): Nhiều bạn nghĩ Math.trunc giống Math.floor. Đúng với số dương, nhưng sai hoàn toàn với số âm!

  • Math.floor(-3.5) sẽ làm tròn xuống số nguyên nhỏ hơn là -4.
  • Math.trunc(-3.5) chỉ đơn giản chặt bỏ .5 để giữ lại -3.
console.log(Math.floor(-3.5)); // -4
console.log(Math.trunc(-3.5)); // -3

6. Math.cbrt(x) — Căn bậc ba

Tìm số nào nhân với chính nó 3 lần thì bằng xx.

console.log(Math.cbrt(27)); // 3
console.log(Math.cbrt(-8)); // -2
  • Kinh nghiệm thực chiến: Bạn có thể tự hỏi: Sao không dùng Math.pow(x, 1/3)? Thử chạy Math.pow(-8, 1/3) đi, bạn sẽ nhận về NaN do cách JavaScript xử lý lũy thừa số phức. Math.cbrt() sinh ra để xử lý chuẩn chỉ cả số âm.

Nhóm 3: Logarit Và Số Mũ Nâng Cao (Logarithmic & Exponential) Nếu anh em làm về tài chính (tính lãi kép), thống kê, hoặc phân tích dữ liệu, đây là những hàm giúp cứu rỗi độ chính xác của phép tính.

7. Math.log10(x) và 8. Math.log2(x) — Logarit cơ số 10 và cơ số 2

Thay vì phải dùng công thức đổi cơ số toán học phiền phức thông qua logarit tự nhiên (Math.log(x) / Math.log(10)), ES6 cung cấp sẵn hai hàm này.

console.log(Math.log10(1000)); // 3
console.log(Math.log2(8));     // 3

Ứng dụng thực chiến: Math.log2 cực kỳ phổ biến trong khoa học máy tính để tính độ sâu của cây nhị phân, hoặc tính số bit cần thiết để lưu trữ một giá trị.

9. Math.log1p(x) — Logarit tự nhiên của (1+x)(1 + x)

Hàm này tính giá trị của ln(1+x)\ln(1 + x).

  • Tại sao cần nó? Trong JavaScript, nếu xx là một số cực kỳ nhỏ (ví dụ 1e-15), phép tính 1 + x sẽ bị lỗi làm tròn thành 1 (do giới hạn lưu trữ dấu câu động). Dẫn đến Math.log(1 + x) sẽ trả về 0. Hàm Math.log1p() sử dụng thuật toán riêng để giữ độ chính xác tuyệt đối cho các số xx siêu nhỏ.

10. Math.expm1(x) — Hàm số mũ trừ đi 1

Ngược lại với log1p, hàm này tính giá trị của ex1e^x - 1. Nó cũng sinh ra nhằm mục đích chống sai số khi xx gần sát số 0.

Nhóm 4: Hình Học Và Lượng Giác Hyperbolic (Geometry & Hyperbolic)11. Math.hypot(...values) — Tính cạnh huyền thượng đẳngHàm này nhận vào một danh sách các số, tính tổng bình phương của chúng rồi rút căn bậc hai: x2+y2+z2+...\sqrt{x^2 + y^2 + z^2 + ...}.

console.log(Math.hypot(3, 4)); // 5 (Tam giác vuông kinh điển 3-4-5)
  • Kinh nghiệm thực chiến: Đừng bao giờ viết Math.sqrt(xx + yy) nữa! Vì nếu xx hoặc yy quá lớn (ví dụ 1e200), việc bình phương chúng lên sẽ làm tràn số thành Infinity. Math.hypot() được thiết kế thông minh để tránh hiện tượng tràn số (overflow/underflow) này. Thêm nữa, nó hỗ trợ tính khoảng cách trong không gian nhiều chiều (3D, 4D...) chỉ bằng cách nhét thêm tham số vào.

12 đến 17. Các hàm Hyperbolic (cosh, sinh, tanh, acosh, asinh, atanh)

Đây là các hàm tính cosin, sin, tang hyperbolic và các hàm ngược (a viết tắt của arc hoặc inverse) của chúng. Khác với lượng giác thông thường dựa trên đường tròn, lượng giác hyperbolic dựa trên đường hyperbola.

console.log(Math.sinh(0)); // 0
console.log(Math.cosh(0)); // 1
console.log(Math.tanh(1)); // 0.7615941559557649
  • Ứng dụng thực chiến: * Trong thiết kế giao diện UI/UX hoặc CSS Animation: Hàm cosh được dùng để tính toán hình dáng chuẩn của một sợi dây xích treo lơ lửng giữa hai điểm (Catenary curve).
  • Trong trí tuệ nhân tạo (AI/Machine Learning): Hàm Math.tanh chính là một trong những Activation Function (Hàm kích hoạt) kinh điển nhất của mạng nơ-ron nhân tạo để chuẩn hóa dữ liệu về khoảng [-1, 1].

Đúc Kết Kinh Nghiệm Từ Những Lần "Vấp Ngã"

Khi làm việc với các tính năng chuyên sâu trong JavaScript, việc nắm rõ interface Math mang lại cho anh em 3 lợi thế lớn:

  1. Hiệu năng vượt trội: Các hàm này được viết bằng mã máy native C++ bên trong V8 Engine, nên tốc độ chạy của chúng luôn nhanh hơn rất nhiều so với việc anh em tự viết thuật toán tương đương bằng JavaScript thuần.
  2. Nói không với Sai Số: Các hàm như hypot, log1p, expm1 là lá chắn vững chắc bảo vệ hệ thống khỏi những con bug sai số tài chính hoặc đồ họa ẩn mình cực khó tìm.
  3. Code Clean chuẩn Senior: Viết code ngắn hơn, ít if-else hơn, tận dụng tối đa sức mạnh có sẵn của ngôn ngữ.

Hy vọng bài viết tổng lực này đã giúp anh em vén bức màn bí mật của đối tượng Math và nhặt được thêm vài món vũ khí mới cho hành trang lập trình của mình.

Anh em đã từng ứng dụng hàm nào trong số các hàm "lạ hoắc" này vào dự án chưa? Hay có pha xử lý toán học nào khiến anh em trầm cảm? Hãy để lại bình luận phía dưới để chúng ta cùng mổ xẻ nhé! Happy Coding! 🚀💻


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.