Cách Làm Tròn Số Trong JavaScript

 Mảng/Array trong JavaScript

Trong bài viết này, chúng ta sẽ khám phá các cách làm tròn số khác nhau trong JavaScript. Trong đó có cách sử dụng các hàm toán học JavaScript và các phương pháp khác để làm tròn đến chữ số thập phân. Chúng ta cũng sẽ đề cập đến những vấn đề cần lưu ý khi làm tròn số thập phân.

JavaScript Rounding

Khi xử lý các giá trị số, đôi khi chúng ta có thể thực hiện các phép tính có kết quả là phân số, cần làm tròn thành một số nguyên - chẳng hạn như khi bạn đang tính giá trung bình hoặc xử lý các số ngẫu nhiên. Trong trường hợp này, đối tượng Math của JavaScript cung cấp một số cách để làm tròn số thành giá trị số nguyên.

Trong các ví dụ hôm nay, chúng ta sẽ sử dụng hai hằng số toán học quan trọng nhất để chứng minh các kiểu làm tròn khác nhau: Pi, là tỷ số giữa chu vi của hình tròn với đường kính của nó và e, là cơ số của logarit tự nhiên và cũng được gọi là "số của Euler". Cả hai giá trị này đều là thuộc tính của đối tượng Math, nhưng chúng ta hãy gán chúng cho một số biến để xử lý chúng dễ dàng hơn:

const PI = Math.PI
const E = Math.E

Bây giờ chúng ta đã xác định các hằng số này, hãy tìm hiểu một số phương pháp làm tròn số trong JavaScript.

Làm tròn số trong JavaScript bằng Math.round

Phương pháp đầu tiên chúng ta tìm hiểu sẽ là Math.round. Đây là tùy chọn đơn giản nhất và chỉ cần làm tròn bất kỳ số nào có phần thập phân thành số nguyên gần nhất. Nó sử dụng quy tắc này: nếu một số nằm chính xác giữa hai số nguyên, nó sẽ được làm tròn lên. Ví dụ: 2,5 sẽ làm tròn thành 3.

Để sử dụng hàm này, chúng ta chỉ cần cung cấp số muốn làm tròn làm đối số:

Math.round(2.3) // rounds down because it's closer to 2
<< 2

Math.round(2.921) // rounds up because it's closer to 3
<< 3

Math.round(2.5) // rounds up because it's exactly halfway
<< 3

Math.round(PI)
<< 3

Math.round(E)
<< 3

Math.round() rất hữu ích nếu bạn muốn làm tròn một số thành giá trị số nguyên gần nhất. Ví dụ: nếu bạn đang tính điểm trung bình cho ba bài kiểm tra, bạn sẽ cộng ba điểm lại và chia cho ba. Phép tính có thể sẽ không cho ra một số nguyên, vì vậy bạn sẽ sử dụng Math.round() để làm tròn nó thành giá trị gần nhất:

const test1 = 86;
const test2 = 93;
const test3 = 95;
const average = Math.round((test1+test2+test3)/3);
<< 91

Làm tròn số bằng Math.floor

Cách thức tiếp theo mà chúng ta sẽ tìm hiểu là Math.floor. Hàm này luôn làm tròn một giá trị xuống số nguyên bên dưới (tên ngụ ý rằng số đang được đẩy xuống sàn):

Math.floor(2.3) // rounds down to 2
<< 2

Math.floor(2.921) // rounds down to 2
<< 2

Math.floor(2.5) // rounds down to 2
<< 2

Math.floor(PI)
<< 3

Math.floor(E)
<< 2

Một cách sử dụng phổ biến của Math.floor là khi tạo các số nguyên ngẫu nhiên. Hàm sẽ làm tròn xuống đảm bảo rằng số nguyên sẽ bắt đầu bằng 0 và mỗi số nguyên sẽ có cơ hội được trả về như nhau. Bắt đầu từ 0 thường hữu ích, vì các mảng trong JavaScript được lập chỉ mục bằng 0, vì vậy việc làm tròn xuống sẽ đảm bảo rằng phần tử đầu tiên trong mảng có thể được chọn. Ví dụ dưới đây cho thấy cách một phần tử ngẫu nhiên có thể được chọn từ một mảng bằng Math.floor:

const fruit = ["🍏","🍌","🍓","🍋","🍐"]

const randomFruit = fruit[Math.floor(Math.random()*fruit.length)]
<< "🍓"

Làm tròn xuống bằng cách sử dụng Math.floor trong đoạn mã trên đảm bảo rằng một chỉ mục từ 0 đến 4 được trả về, vì vậy mọi phần tử trong mảng đều có cơ hội được chọn như nhau.

Làm tròn số bằng Math.ceil

Nói về làm tròn, đây chính xác là những gì Math.ceil làm. Như ngụ ý từ cái tên, hàm này sẽ ngược lại với Math.floor và làm tròn số lên cao. Phương pháp này hoạt động theo cùng một cách như tất cả các phương pháp khác. Chỉ cần cung cấp số bạn muốn làm tròn làm đối số:

Math.ceil(2.3) // rounds up to 3
<< 3

Math.ceil(2.921) // rounds up to 3
<< 3

Math.ceil(2.5) // rounds up to 3
<< 3

Math.ceil(PI)
<< 4

Math.ceil(E)
<< 3

Nhưng khi nào bạn cần làm tròn một số lên cao hơn? Một cách sử dụng phổ biến là nếu bạn cần tính xem bạn cần bao nhiêu thùng chứa cho một thứ gì đó. Ví dụ: giả sử bạn có một trang web âm nhạc bao gồm danh sách phát và mỗi danh sách phát có mười bài hát trên đó. Nếu ai đó tải lên 82 bài hát, bạn cần tính xem có bao nhiêu danh sách phát để tạo. Điều này được thực hiện bằng cách chia số lượng bài hát cho 10 (số lượng bài hát trên mỗi danh sách phát):

const songsPerPlaylist = 10;
const numberOfSongs = 82;
const numberOfPlaylists = numberOfSongs/songsPerPlaylist;
<< 8.2

Sử dụng Math.round sẽ làm tròn con số này xuống 8… nhưng sau đó chúng ta sẽ không có danh sách phát cho hai bài hát cuối cùng! Trong những trường hợp như thế này, chúng ta luôn cần phải làm tròn cao hơn để có thêm một vùng chứa cho bất kỳ phần còn lại nào:

const numberOfPlaylists = Math.ceil(numberOfSongs/songsPerPlaylist);
<< 9

Làm tròn số với Math.trunc

Phương pháp tiếp theo mà chúng ta sẽ tìm hiểu là Math.trunc. Đây không hoàn toàn là một hàm chuyên biệt để làm tròn số thập phân; thực ra nó sẽ cắt bớt số được cung cấp dưới dạng đối số. Về cơ bản, nó chỉ loại bỏ phần thập phân của số, chỉ để lại phần nguyên, như có thể thấy trong các ví dụ dưới đây:

Math.trunc(2.3) // just leaves 2
<< 2

Math.trunc(2.921) // just leaves 2, even though it's closer to 3
<< 2

Math.trunc(2.5) // just leaves 2
<< 2

Math.trunc(PI)
<< 3

Math.trunc(E)
<< 2

Thoạt nhìn, Math.trunc có vẻ giống với Math.floor; chắc chắn các ví dụ được đưa ra cho đến nay đều cho kết quả như nhau. Tuy nhiên, hai phương thức này hoạt động khác nhau khi một giá trị âm được cung cấp làm đối số, như có thể thấy trong ví dụ dưới đây:

Math.floor(-2.3) // rounds DOWN to -3
<< -3

Math.trunc(-2.3) // removes the decimal part, leaving just -2
<< -2

Sự khác biệt xảy ra bởi vì, khi một số âm được làm tròn xuống bằng Math.floor, nó sẽ đi xuống số nguyên thấp nhất tiếp theo, trong khi việc cắt bớt một giá trị âm tương đương với việc làm tròn nó cao hơn.

Math.ceil trả về cùng giá trị với Math.trunc khi đối số là số âm:

Math.trunc(-2.3) // removes the decimal part, leaving just -2
<< -2

Math.ceil(-2.3) // rounds UP to -2
<< -2

Tất cả các phương thức này có thể rất hữu ích, nhưng chúng có hạn chế là chúng luôn trả về các giá trị nguyên. Điều gì sẽ xảy ra nếu chúng ta muốn làm tròn một số đến một số chữ số thập phân hoặc số có nghĩa nhất định?

Làm tròn số sau dấu phẩy trong JavaScript

Chúng ta đã thấy rằng Math.round sẽ làm tròn số đến số nguyên gần nhất. Rất tiếc, đối tượng Math không cung cấp bất kỳ phương pháp nào để làm tròn số chính xác hơn đến một số chữ số nhất định sau dấu phẩy. Rất may, kiểu Number có một vài phương thức tích hợp có thể thực hiện việc này. Hãy xem chúng.

Làm tròn số sau dấu phẩy với Number.toFixed

Đây là một phương pháp số, có nghĩa là nó được gọi bằng chính số đó. Nó làm tròn một số thập phân đến một số vị trí nhất định sau dấu phẩy, được cung cấp dưới dạng đối số:

2.4387587.toFixed(2) // rounds to 2 decimal places
<< "2.44"

Một điều cần lưu ý là giá trị được trả về dưới dạng một string. Bạn có thể giải quyết vấn đề này bằng cách gói lệnh gọi phương thức trong hàm Number, hàm này sẽ chuyển đổi kết quả trở lại thành một số:

Number(2.4387587.toFixed(2))
<< 2.44

Một điều khác cần chú ý: nếu bạn cố gắng áp dụng phương pháp này cho một số đã là số nguyên, bạn sẽ gặp lỗi nếu chỉ sử dụng một dấu chấm duy nhất để gọi phương thức:

2.toFixed(2)
<< SyntaxError

Bạn không thể gọi các phương thức trên số nguyên bằng cách sử dụng một dấu chấm, vì không rõ dấu chấm là toán tử lệnh gọi phương thức hay dấu thập phân. Để giải quyết vấn đề này, bạn có thể đặt số nguyên trong dấu ngoặc đơn hoặc sử dụng hai dấu chấm để rõ ràng rằng bạn đang gọi một phương thức thay vì viết ra một chữ số với dấu thập phân:

(2).toFixed(2)
<< "2.00"

2..toFixed(2)
<< "2.00"

Nếu không có đối số nào được cung cấp, số sẽ được làm tròn thành số nguyên gần nhất (nhưng được trả về dưới dạng chuỗi): Wenn kein Argument angegeben wird, wird die Zahl auf die nächste Ganzzahl gerundet (aber als Zeichenfolge zurückgegeben):

PI.toFixed()
<< "3"

E.toFixed()
<< "3"

Một ví dụ khác cho trường hợp cần làm tròn đến vị trí nhất định sau dấu phẩy là khi giao dịch với tiền tệ - ví dụ: nếu bạn muốn cung cấp giá của thứ gì đó bằng đô la Mỹ chính xác đến xu gần nhất. Giả sử bạn có một trang web thương mại điện tử đang chạy chương trình khuyến mại giảm giá 15% cho bất kỳ thứ gì trong giỏ hàng. Giá đã chiết khấu có thể cần làm tròn trước khi hiển thị:

const item1Price = 2.99
const item2Price = 4.99
const item3Price = 6.20

const totalPrice = item1Price + item2Price + item3Price
const discountedPrice = 0.85 * totalPrice
<< 12.052999999999999

Vấn đề này có thể được xử lý dễ dàng với Number.toFixed:

const discountedPrice = (0.85 * totalPrice).toFixed(2)
<< "12.05" 

Làm tròn số sau dấu phẩy với Number.toPrecision

Phương thức Number.toPrecision hoạt động theo cách tương tự như phương thức Number.toFixed, nhưng nó làm tròn số thành một số cố định của tập hợp số đáng kể.

Nếu bạn chưa biết về số đáng kể, cơ bản mà nói, đây là tên gọi những số chỉ sử dụng các chữ số khác không đầu tiên. Đối với các số lớn, kết quả cuối cùng cũng sẽ được thêm vào bằng các số 0. Ví dụ, số 53,863 được làm tròn thành hai con số đáng kể sẽ trở thành 54,000. Điều này là do 5 và 3 là hai chữ số đầu tiên khác 0 và nó làm tròn vì chữ số tiếp theo là 8. Chúng ta cần thêm các số 0 vào cuối để đảm bảo giá trị làm tròn là một giá trị gần đúng hợp lý với số ban đầu.

Bạn cũng có thể làm tròn số thập phân theo cách tương tự. Ví dụ: 0,00000623978 sẽ làm tròn thành 0,0000062 thành hai số đáng kể vì 6 và 2 là các chữ số khác không đầu tiên và nó làm tròn xuống vì chữ số tiếp theo là 3.

Để sử dụng phương thức này, chỉ cần gọi nó trên số, cung cấp số lượng các số liệu đáng kể làm đối số (hãy nhớ rằng các số nguyên cần được đặt trong dấu ngoặc đơn trước khi gọi một phương thức trên chúng):

(53863).toPrecision(2)
<< "5.4e+4"

0.00000623978.toPrecision(2)
<< 0.0000062"

Lưu ý rằng tất cả các giá trị được trả về dưới dạng chuỗi và có thể sử dụng ký hiệu hàm mũ - chẳng hạn như “5,4e + 4” thay vì “54000”.

Như trước đây, chúng ta có thể đảm bảo rằng một số được trả về bằng cách gói lệnh gọi phương thức trong hàm Number:

Number((53863).toPrecision(2))
<< 54000

Một cách dùng phổ biến của số đáng kể là khi bạn đang xử lý các số lớn và bạn không chắc chúng sẽ lớn đến mức nào. Ví dụ: giả sử bạn muốn báo cáo số lần bài đăng mới nhất của bạn đã được “thích”, bạn có làm tròn nó thành 10, 100 hay 1000 gần nhất không? Theo một cách nào đó, điều này phụ thuộc vào mức độ phổ biến của nó; bạn không muốn làm tròn nó thành 100 gần nhất nếu nó chỉ nhận được 8 lượt thích, nhưng nếu nó nhận được hàng nghìn lượt thích thì việc làm tròn nó đến gần nhất là 10. Giải pháp là làm tròn nó thành một con số đáng kể:

const unpopularPost = 8;
const quitePopularPost = 387;
const poplularPost = 79671;

Number(unpopularPost.toPrecision(1))
<< 8
Number(quitePopularPost.toPrecision(1))
<< 400
Number(poplularPost.toPrecision(1))
<< Number(poplularPost.toPrecision(1))
<< 80000

Vấn đề với việc làm tròn số thập phân trong JavaScript

Có một số điều cần chú ý khi làm tròn số trong JavaScript (hoặc bất kỳ ngôn ngữ lập trình nào). Như bạn có thể biết, máy tính lưu trữ tất cả dữ liệu - bao gồm cả số - dưới dạng biểu diễn nhị phân. JavaScript lưu trữ các số dưới dạng các giá trị nhị phân chính xác đơn 32 bit.

Vấn đề với điều này là một số số cơ số 10 không thể được biểu diễn chính xác với cơ số 2. Điều này thường không gây ra bất kỳ vấn đề nào, nhưng nó gây ra một số kết quả kỳ lạ như sau:

0.1 + 0.2 === 0.3
<< false

Điều này là do 0,1 và 0,2 không thể được biểu diễn chính xác trong hệ nhị phân và sẽ xảy ra lỗi nhỏ khi cộng chúng.

Đối tượng Math có một phương thức khác được gọi là fround, trả về số gần nhất có thể được biểu diễn bằng hệ 32-bit. Ví dụ: 0,6125 có thể được biểu diễn chính xác trong hệ nhị phân là 0,101, vì vậy giá trị này sẽ trả về cùng một giá trị:

Math.fround(0.625)
<< 0.625

Tuy nhiên, như chúng ta đã thấy ở trên, 0,1 không thể được biểu diễn chính xác bằng 32 bit. Math.fround cho chúng ta thấy con số gần nhất có thể được biểu diễn:

Math.fround(0.1)
<< 0.10000000149011612

Như bạn có thể thấy, nó rất gần với 0,1, nhưng cao hơn một chút. Trong hầu hết các trường hợp thực tế, điều này sẽ không gây ra bất kỳ vấn đề nào, nhưng đôi khi nó có thể gây ra một số hành vi kỳ lạ khi bạn cố gắng làm tròn một số con số:

3.55.toFixed(1) // should round UP to 3.6
<< "3.5"

Điều này xảy ra vì số thập phân 3,55 không thể được biểu diễn chính xác bằng cách sử dụng 32 bit. Chúng tôi có thể sử dụng Math.fround để xem nó thực sự được biểu diễn như thế nào:

Math.fround(3.55)
<< 3.549999952316284

Như bạn có thể thấy, nó thực sự được biểu thị bằng số dấu phẩy động 3.549999952316284, làm tròn xuống 3.5.

Những vấn đề này với việc làm tròn số trong JavaScript không xảy ra quá thường xuyên, nhưng chúng chắc chắn là điều bạn nên biết nếu bạn gặp nhiều trường hợp cần làm tròn - đặc biệt khi rất cần đến kết quả chính xác nhất có thể.

Tôi nên sử dụng phương pháp nào để làm tròn số?

Với tất cả các phương pháp làm tròn được trình bày trong bài viết này, bạn có thể tự hỏi cách nào là tốt nhất để sử dụng. Như mọi khi, câu trả lời là, "Tùy".

Nếu bạn chỉ muốn làm tròn một số đến số nguyên gần nhất, dùng Math.round là phù hợp nhất, nhưng bạn cũng nên cân nhắc sử dụng Math.floor hoặc  Math.ceil nếu bạn luôn muốn làm tròn thấp hoặc cao hơn, bất kể phần thập phân là gì. Và hãy cân nhắc sử dụng Math.trunc nếu bạn dự định làm tròn số âm.

Nếu bạn cần làm tròn đến một số chữ số thập phân nhất định hoặc số đáng kể, bạn sẽ phải sử dụng Number.toFixed hoặc Number.toPrecision. Nhưng hãy lưu ý rằng hai phương thức này được gọi bằng số và trả về một chuỗi.

Bạn có thể xem ví dụ về tất cả các kiểu làm tròn khác nhau được đề cập trong bài viết này trong bản demo CodePen sau.

Theo Sitepoint

Nhận xét

Bài đăng phổ biến