Làm tròn đến tối đa 2 chữ số thập phân (chỉ khi cần thiết)


2758

Tôi muốn làm tròn tối đa 2 chữ số thập phân, nhưng chỉ khi cần thiết .

Đầu vào:

10
1.7777777
9.1

Đầu ra:

10
1.78
9.1

Làm thế nào tôi có thể làm điều này trong JavaScript?


22
Tôi đã thực hiện một câu đố với nhiều kỹ thuật được cung cấp dưới dạng giải pháp tại đây ... để bạn có thể so sánh: fiddle
dsdsdsdsd

37
Không ai có thể nhận thức được Number.EPSILON. Sử dụng Math.round( num * 100 + Number.EPSILON ) / 100.
cronvel

3
Đối với những người đọc mới, bạn không thể làm điều này trừ khi bạn có kết quả kiểu chuỗi . Toán học dấu phẩy động trên các biểu diễn nội bộ nhị phân của các số có nghĩa là luôn có các số không thể được biểu diễn dưới dạng số thập phân gọn.
Walf

9
@cronvel Bạn có thể giải thích lý do sử dụng Number.EPSILONở đây?
Bruce Sun

5
Tôi rơi xuống hố thỏ và kiểm tra một số câu trả lời thú vị hơn trên trang này (chỉ trang đầu tiên). Đây là một Codepen . Gợi ý: câu trả lời càng tăng, cơ hội hoạt động đúng càng thấp.
Adam Jagosz

Câu trả lời:


3493

Sử dụng Math.round(num * 100) / 100

Chỉnh sửa: để đảm bảo những thứ như vòng 1.005 chính xác, chúng tôi sử dụng

Math.round((num + Number.EPSILON) * 100) / 100


395
Mặc dù điều này sẽ hoạt động trong hầu hết các trường hợp, nhưng nó sẽ không hoạt động cho 1.005 mà cuối cùng sẽ trở thành 1 thay vì 1.01
James

83
@James Wow thật kỳ lạ - Tôi đang làm việc trong bảng điều khiển dành cho nhà phát triển Chrome và tôi nhận thấy rằng 1.005 * 100 = 100.49999999999999. Math.round (100.49999999999999) ước tính thành 100, trong khi Math.round (100.5) ước tính là 101. IE9 cũng làm điều tương tự. Điều này là do sự kỳ lạ của dấu chấm động trong javascript
stinkycheeseman

153
Một cách giải quyết đơn giản. Trong 2 dp, sử dụng Math.round((num + 0.00001) * 100) / 100. Hãy thử Math.round((1.005 + 0.00001) * 100) / 100Math.round((1.0049 + 0.00001) * 100) / 100
mrkschan

31
@mrkschan Tại sao nó hoạt động, và nó có thể đánh lừa được tất cả các con số không?
CMCDragonkai

86
Đối với những người không hiểu thì kỹ thuật này được gọi là nhân rộng. Về cơ bản, câu trả lời ở đây là đưa hai hình qua dấu thập phân biến hình thành một số nguyên để tránh tất cả các vấn đề về dấu phẩy động điên rồ, làm tròn nó và sau đó dịch lại thành số trước đó bằng cách chia cho 100 và bạn có Trả lời 2dp.
Alex_Nabu

3064

Nếu giá trị là một loại văn bản:

parseFloat("123.456").toFixed(2);

Nếu giá trị là một số:

var numb = 123.23454;
numb = numb.toFixed(2);

Có một nhược điểm là các giá trị như 1.5 sẽ cho "1,50" làm đầu ra. Một bản sửa lỗi được đề xuất bởi @minitech:

var numb = 1.5;
numb = +numb.toFixed(2);
// Note the plus sign that drops any "extra" zeroes at the end.
// It changes the result (which is a string) into a number again (think "0 + foo"),
// which means that it uses only as many digits as necessary.

Có vẻ như Math.roundlà một giải pháp tốt hơn. Nhưng nó không phải như vậy! Trong một số trường hợp, nó sẽ KHÔNG làm tròn chính xác:

Math.round(1.005 * 1000)/1000 // Returns 1 instead of expected 1.01!

toFixed () cũng sẽ KHÔNG làm tròn chính xác trong một số trường hợp (được thử nghiệm trong Chrome v.55.0.2883.87)!

Ví dụ:

parseFloat("1.555").toFixed(2); // Returns 1.55 instead of 1.56.
parseFloat("1.5550").toFixed(2); // Returns 1.55 instead of 1.56.
// However, it will return correct result if you round 1.5551.
parseFloat("1.5551").toFixed(2); // Returns 1.56 as expected.

1.3555.toFixed(3) // Returns 1.355 instead of expected 1.356.
// However, it will return correct result if you round 1.35551.
1.35551.toFixed(2); // Returns 1.36 as expected.

Tôi đoán, điều này là bởi vì 1.555 thực sự là một cái gì đó giống như float 1.55499994 đằng sau hậu trường.

Giải pháp 1 là sử dụng tập lệnh với thuật toán làm tròn yêu cầu, ví dụ:

function roundNumber(num, scale) {
  if(!("" + num).includes("e")) {
    return +(Math.round(num + "e+" + scale)  + "e-" + scale);
  } else {
    var arr = ("" + num).split("e");
    var sig = ""
    if(+arr[1] + scale > 0) {
      sig = "+";
    }
    return +(Math.round(+arr[0] + "e" + sig + (+arr[1] + scale)) + "e-" + scale);
  }
}

https://plnkr.co/edit/uau8BlS1cqbvWPCHJeOy?p=preview

LƯU Ý: Đây không phải là một giải pháp phổ quát cho tất cả mọi người. Có một số thuật toán làm tròn khác nhau, việc thực hiện của bạn có thể khác nhau, tùy thuộc vào yêu cầu của bạn. https://en.wikipedia.org/wiki/Rounding

Giải pháp 2 là tránh tính toán mặt trước và kéo các giá trị làm tròn từ máy chủ phụ trợ.


81
Cách tiếp cận này (cách kết hợp) là tốt và hiệu quả với tôi, nhưng nó đặc biệt không tuân thủ yêu cầu ban đầu là "chỉ khi cần thiết". (Nó làm tròn 1,5 đến 1,50, phá vỡ thông số kỹ thuật.)
Per Lundberg

29
Đối với yêu cầu "khi cần thiết", hãy làm điều này: parseFloat(number.toFixed(decimalPlaces)); @PerLundberg
Onur Yıldırım

36
parseFloat("55.555").toFixed(2)trả về "55.55"trong bảng điều khiển Chrome dev.
Levi Botelho

22
Không có lợi thế khi sử dụng toFixed thay vì Math.round; toFixed dẫn đến các vấn đề làm tròn khá giống nhau (thử chúng với 5.555 và 1.005), nhưng giống như 500x (không đùa) chậm hơn Math.round ... Có vẻ như câu trả lời @MarkG chính xác hơn ở đây.
Pierre

17
toFixed không "đôi khi" trả về một chuỗi, nó luôn trả về một chuỗi.
McGuireV10

464

Bạn có thể dùng

function roundToTwo(num) {    
    return +(Math.round(num + "e+2")  + "e-2");
}

Tôi đã tìm thấy điều này trên MDN . Cách của họ tránh được vấn đề với 1.005 đã được đề cập .

roundToTwo(1.005)
1.01
roundToTwo(10)
10
roundToTwo(1.7777777)
1.78
roundToTwo(9.1)
9.1
roundToTwo(1234.5678)
1234.57

13
@Redsandro, +(val)là sự ép buộc tương đương với việc sử dụng Number(val). Nối "e-2" với một số dẫn đến một chuỗi cần được chuyển đổi trở lại thành một số.
Jack

41
Xin lưu ý rằng những chiếc phao lớn và nhỏ sẽ tạo ra NaN kể từ đó, ví dụ + "1e-21 + 2" sẽ không được phân tích cú pháp chính xác.
Pierre

16
Bạn đã 'giải quyết' 1,005 'vấn đề', nhưng đã giới thiệu một vấn đề mới: bây giờ, trong bảng điều khiển Chrome, roundToTwo(1.0049999999999999)xuất hiện là 1.01 (chắc chắn, kể từ đó 1.0049999999999999 == 1.005). Dường như với tôi rằng số float bạn nhận được nếu bạn gõ num = 1.005'Rõ ràng' 'nên làm tròn thành 1,00, bởi vì giá trị chính xác của num nhỏ hơn 1,005. Tất nhiên, đối với tôi, dường như chuỗi '1.005' 'rõ ràng' 'nên được làm tròn thành 1.01. Việc những người khác nhau dường như có trực giác khác nhau về hành vi đúng thực tế ở đây là một phần lý do tại sao nó phức tạp.
Đánh dấu Amery

35
Không có số (dấu phẩy động) ở giữa 1.00499999999999991.005do đó, theo định nghĩa, chúng là cùng một số. Điều này được gọi là cắt giảm.
Azmisov

6
@Azmisov nói đúng. Trong khi 1.00499 < 1.005true, 1.0049999999999999 < 1.005đánh giá để false.
falconepl

146

Câu trả lời của MarkG là câu trả lời đúng. Đây là một phần mở rộng chung cho bất kỳ số vị trí thập phân.

Number.prototype.round = function(places) {
  return +(Math.round(this + "e+" + places)  + "e-" + places);
}

Sử dụng:

var n = 1.7777;    
n.round(2); // 1.78

Bài kiểm tra đơn vị:

it.only('should round floats to 2 places', function() {

  var cases = [
    { n: 10,      e: 10,    p:2 },
    { n: 1.7777,  e: 1.78,  p:2 },
    { n: 1.005,   e: 1.01,  p:2 },
    { n: 1.005,   e: 1,     p:0 },
    { n: 1.77777, e: 1.8,   p:1 }
  ]

  cases.forEach(function(testCase) {
    var r = testCase.n.round(testCase.p);
    assert.equal(r, testCase.e, 'didn\'t get right number');
  });
})

20
Pierre nêu vấn đề nghiêm trọng với câu trả lời của MarkG.
DSjoerg

9
Lưu ý: Nếu bạn không muốn thay đổi Number.prototype - chỉ cần viết hàm này dưới dạng hàm: function round(number, decimals) { return +(Math.round(number + "e+" + decimals) + "e-" + decimals); }
Philipp Tsipman

2
Một cách tuyệt vời để mở rộng này. Bạn có thể kiểm tra xem số thập phân có âm hay không, đảo ngược e + và e-. Khi đó n = 115; n.round (-2); sẽ sản xuất 100
Lee Louviere

3
Hãy thử n: 1e + 19 - nó trả về NaN
DavidJ

3
Thuật toán này luôn làm tròn (thay vì từ 0). Vì vậy, trong trường hợp số âm, đầu ra không như bạn mong đợi:(-1.005).round(2) === -1
Aleksej Komarov

123

Bạn nên sử dụng:

Math.round( num * 100 + Number.EPSILON ) / 100

Không ai có thể nhận thức được Number.EPSILON.

Ngoài ra, điều đáng chú ý là đây không phải là một sự kỳ lạ của JavaScript như một số người đã nêu.

Đó chỉ đơn giản là cách các số dấu phẩy động hoạt động trong máy tính. Giống như 99% ngôn ngữ lập trình, JavaScript không có số dấu phẩy động tại nhà ; nó dựa vào CPU / FPU cho điều đó. Một máy tính sử dụng nhị phân và trong nhị phân, không có bất kỳ số nào giống như 0.1, nhưng chỉ là xấp xỉ nhị phân cho điều đó. Tại sao? Vì lý do tương tự, hơn 1/3 không thể được viết bằng số thập phân: giá trị của nó là 0,3333333 ... với vô số ba mươi.

Dưới đây đi Number.EPSILON. Con số đó là sự khác biệt giữa 1 và số tiếp theo tồn tại trong các số dấu phẩy động chính xác kép. Đó là: Không có số giữa 1và 1 + Number.EPSILON.

BIÊN TẬP:

Như được hỏi trong các ý kiến, chúng ta hãy làm rõ một điều: việc thêm Number.EPSILONchỉ có liên quan khi giá trị làm tròn là kết quả của phép toán số học, vì nó có thể nuốt một số lỗi delta dấu phẩy động.

Nó không hữu ích khi giá trị đến từ một nguồn trực tiếp (ví dụ: nghĩa đen, đầu vào của người dùng hoặc cảm biến).

EDIT (2019):

Giống như @maganap và một số người đã chỉ ra, tốt nhất nên thêm Number.EPSILONtrước khi nhân:

Math.round( ( num + Number.EPSILON ) * 100 ) / 100

EDIT (tháng 12 năm 2019):

Gần đây, tôi sử dụng một chức năng tương tự như chức năng này để so sánh các số nhận biết epsilon:

const ESPILON_RATE = 1 + Number.EPSILON ;
const ESPILON_ZERO = Number.MIN_VALUE ;

function epsilonEquals( a , b ) {
  if ( Number.isNaN( a ) || Number.isNaN( b ) ) {
    return false ;
  }
  if ( a === 0 || b === 0 ) {
    return a <= b + EPSILON_ZERO && b <= a + EPSILON_ZERO ;
  }
  return a <= b * EPSILON_RATE && b <= a * EPSILON_RATE ;
}

Ca sử dụng của tôi là một xác nhận + xác thực dữ liệu lib tôi đang phát triển trong nhiều năm.

Trong thực tế, trong mã tôi đang sử dụng ESPILON_RATE = 1 + 4 * Number.EPSILONEPSILON_ZERO = 4 * Number.MIN_VALUE(gấp bốn lần epsilon), vì tôi muốn một trình kiểm tra đẳng thức đủ lỏng để tích lũy lỗi dấu phẩy động.

Cho đến nay, nó trông hoàn hảo cho tôi. Tôi mong nó sẽ có ích.


1
@palota bạn có thể định nghĩa một số tine thực sự như cronvel EPSILON đang đề cập
Daniel San

3
Đây là câu trả lời đúng! Vấn đề tự nó liên quan đến cách các số float hoạt động bên trong, chứ không phải về javascript
Daniel San

22
Trên thực tế, bạn phải thêm epsilon TRƯỚC nhân với 100 Math.round( (num + Number.EPSILON) * 100) / 100.. Tôi cũng đồng ý đây là phương pháp đúng để làm tròn chính xác (mặc dù nó không chính xác những gì được hỏi trong câu hỏi này).
maganap

2
@cronvel Hmm. Bạn đúng; sự lựa chọn hướng đi ý nghĩa Vì vậy, tôi đoán rằng sự phản đối còn lại của tôi chỉ là làm điều này chỉ có ý nghĩa nếu bạn làm việc trong một miền mà bạn có lý do chính đáng để nghĩ rằng giá trị của bạn là "số tròn". Nếu bạn biết rằng đầu vào của bạn là kết quả của các phép toán số học đơn giản trên các số có số thập phân nhỏ, thì chắc chắn, có lẽ bạn chỉ có 0.004999999999999999kết quả là lỗi dấu phẩy động gộp và kết quả đúng về mặt toán học có lẽ là 0,005. Nếu đó là một đọc từ một cảm biến? Không nhiều lắm.
Đánh dấu Amery

1
@marchaos Nó không thất bại: nửa luôn làm tròn lên . Ví dụ Math.round(1.5)= 2, nhưng Math.round(-1.5)= -1. Vì vậy, điều này là hoàn toàn phù hợp. Ở đây -1 lớn hơn -2, giống như -1000 lớn hơn -1000,01. Không được nhầm lẫn với số tuyệt đối lớn hơn .
cronvel

84

Người ta có thể sử dụng .toFixed(NumberOfDecimalPlaces).

var str = 10.234.toFixed(2); // => '10.23'
var number = Number(str); // => 10.23

4
Cũng bởi vì nó thêm các số 0 ở cuối, đây không phảicâu hỏi ban đầu .
Alastair Maw

2
Nhưng các số 0 ở cuối dễ dàng bị tước bằng biểu thức chính quy, tức là `Số (10.10000.toFixed (2) .replace (/ 0 + $ /, ''))` => 10.1
Chad

1
@daniel câu trả lời hàng đầu là giống nhau (như bây giờ) nhưng nó không thay đổi chính xác, hãy thử +(1.005).toFixed(2)trả về 1thay vì 1.01.
Emile Bergeron

3
@ChadMcElligott: regex của bạn không hoạt động tốt với số nguyên: Number(9).toFixed(2).replace(/0+$/, '')=> "9."
Jacob van Lingen

Có vẻ như điều này không tròn. Về cơ bản, nó chỉ là cắt ngắn. Trong một số trường hợp, cũng đủ! Cảm ơn.
iedmrc

78

Câu hỏi này rất phức tạp.

Giả sử chúng ta có một hàm, roundTo2DP(num)lấy một số float làm đối số và trả về giá trị được làm tròn đến 2 vị trí thập phân. Mỗi biểu thức này nên đánh giá cái gì?

  • roundTo2DP(0.014999999999999999)
  • roundTo2DP(0.0150000000000000001)
  • roundTo2DP(0.015)

Câu trả lời 'rõ ràng' là ví dụ đầu tiên nên làm tròn đến 0,01 (vì nó gần 0,01 hơn 0,02) trong khi hai ví dụ còn lại làm tròn đến 0,02 (vì 0,0150000000000000001 gần với 0,02 hơn 0,01 và vì 0,015 nằm chính xác giữa chúng và có một quy ước toán học rằng những con số như vậy được làm tròn lên).

Cái bẫy mà bạn có thể đoán được là roundTo2DP không thể thực hiện được để đưa ra những câu trả lời rõ ràng đó, bởi vì cả ba số được truyền cho nó đều là cùng một số . Các số dấu phẩy động nhị phân của IEEE 754 (loại được sử dụng bởi JavaScript) không thể biểu thị chính xác hầu hết các số không nguyên và vì vậy cả ba chữ số ở trên được làm tròn thành một số dấu phẩy động hợp lệ gần đó. Con số này, như nó xảy ra, là chính xác

0,01199999999999999944488848768742172978818416595458984375

gần với 0,01 hơn 0,02.

Bạn có thể thấy rằng cả ba số đều giống nhau trên bảng điều khiển trình duyệt, trình bao Node hoặc trình thông dịch JavaScript khác. Chỉ cần so sánh chúng:

> 0.014999999999999999 === 0.0150000000000000001
true

Vì vậy, khi tôi viết m = 0.0150000000000000001, giá trị chính xác củam cái mà tôi kết thúc gần 0.01với nó hơn 0.02. Tuy nhiên, nếu tôi chuyển đổi mthành Chuỗi ...

> var m = 0.0150000000000000001;
> console.log(String(m));
0.015
> var m = 0.014999999999999999;
> console.log(String(m));
0.015

... Tôi nhận được 0,015, làm tròn thành 0,02 và đáng chú ý không phải là số thập phân 56 mà tôi đã nói trước đó nói rằng tất cả các số này đều chính xác bằng. Vậy ma thuật đen tối này là gì?

Câu trả lời có thể được tìm thấy trong đặc tả ECMAScript, trong phần 7.1.12.1: ToString được áp dụng cho loại Số . Ở đây các quy tắc để chuyển đổi một số Số m thành Chuỗi được đặt ra. Phần quan trọng là điểm 5, trong đó một số nguyên s được tạo ra có chữ số sẽ được sử dụng trong chuỗi đại diện của m :

Đặt n , ks là các số nguyên sao cho k ≥ 1, 10 k -1s <10 k , giá trị Số cho s × 10 n - kmk càng nhỏ càng tốt. Lưu ý rằng k là số chữ số trong biểu diễn thập phân của s , s đó không chia hết cho 10 và chữ số có nghĩa nhỏ nhất của s không nhất thiết phải được xác định duy nhất bởi các tiêu chí này.

Phần quan trọng ở đây là yêu cầu " k càng nhỏ càng tốt". Yêu cầu đó là bao nhiêu là một yêu cầu, với một Số m, giá trị của String(m)phải có số chữ số ít nhất có thể trong khi vẫn đáp ứng yêu cầu đó Number(String(m)) === m. Vì chúng ta đã biết rằng0.015 === 0.0150000000000000001 , giờ thì rõ ràng tại sao String(0.0150000000000000001) === '0.015'phải đúng.

Tất nhiên, không có cuộc thảo luận nào đã trả lời trực tiếp những gì roundTo2DP(m) nên trả lại. Nếu mgiá trị chính xác của là 0,01199999999999999944488848768742172978818416595458984375, nhưng đại diện Chuỗi của nó là '0,015', thì đó là gì câu trả lời đúng - về mặt toán học, thực tế, triết học hay bất cứ điều gì - khi chúng ta làm tròn nó thành hai số thập phân?

Không có câu trả lời đúng duy nhất cho điều này. Nó phụ thuộc vào trường hợp sử dụng của bạn. Bạn có thể muốn tôn trọng biểu diễn Chuỗi và làm tròn lên khi:

  • Giá trị được đại diện là rời rạc, ví dụ như một lượng tiền tệ trong một loại tiền có 3 chữ số thập phân như dinar. Trong trường hợp này, giá trị thực của một Số như 0,015 0,015 và đại diện 0,0119999999 ... mà nó nhận được ở dấu phẩy động nhị phân là lỗi làm tròn. (Tất nhiên, nhiều người sẽ tranh luận, một cách hợp lý, rằng bạn nên sử dụng thư viện thập phân để xử lý các giá trị đó và không bao giờ biểu thị chúng dưới dạng Số dấu phẩy động nhị phân ở vị trí đầu tiên.)
  • Giá trị được gõ bởi một người dùng. Trong trường hợp này, một lần nữa, số thập phân chính xác được nhập là "đúng" hơn so với biểu diễn dấu phẩy động nhị phân gần nhất.

Mặt khác, bạn có thể muốn tôn trọng giá trị dấu phẩy động nhị phân và làm tròn xuống khi giá trị của bạn từ thang đo liên tục vốn có - ví dụ, nếu đó là cách đọc từ cảm biến.

Hai cách tiếp cận này yêu cầu mã khác nhau. Để tôn trọng biểu diễn Chuỗi của Số, chúng tôi có thể (với khá nhiều mã tinh tế hợp lý) thực hiện làm tròn số của chúng tôi hoạt động trực tiếp trên biểu diễn Chuỗi, chữ số theo chữ số, sử dụng cùng thuật toán bạn đã sử dụng ở trường khi bạn được dạy cách làm tròn số. Dưới đây là một ví dụ tôn trọng yêu cầu của OP về việc biểu thị số đến 2 vị trí thập phân "chỉ khi cần thiết" bằng cách tước các số 0 ở sau dấu thập phân; tất nhiên, bạn có thể cần phải điều chỉnh nó theo nhu cầu chính xác của mình.

/**
 * Converts num to a decimal string (if it isn't one already) and then rounds it
 * to at most dp decimal places.
 *
 * For explanation of why you'd want to perform rounding operations on a String
 * rather than a Number, see http://stackoverflow.com/a/38676273/1709587
 *
 * @param {(number|string)} num
 * @param {number} dp
 * @return {string}
 */
function roundStringNumberWithoutTrailingZeroes (num, dp) {
    if (arguments.length != 2) throw new Error("2 arguments required");

    num = String(num);
    if (num.indexOf('e+') != -1) {
        // Can't round numbers this large because their string representation
        // contains an exponent, like 9.99e+37
        throw new Error("num too large");
    }
    if (num.indexOf('.') == -1) {
        // Nothing to do
        return num;
    }

    var parts = num.split('.'),
        beforePoint = parts[0],
        afterPoint = parts[1],
        shouldRoundUp = afterPoint[dp] >= 5,
        finalNumber;

    afterPoint = afterPoint.slice(0, dp);
    if (!shouldRoundUp) {
        finalNumber = beforePoint + '.' + afterPoint;
    } else if (/^9+$/.test(afterPoint)) {
        // If we need to round up a number like 1.9999, increment the integer
        // before the decimal point and discard the fractional part.
        finalNumber = Number(beforePoint)+1;
    } else {
        // Starting from the last digit, increment digits until we find one
        // that is not 9, then stop
        var i = dp-1;
        while (true) {
            if (afterPoint[i] == '9') {
                afterPoint = afterPoint.substr(0, i) +
                             '0' +
                             afterPoint.substr(i+1);
                i--;
            } else {
                afterPoint = afterPoint.substr(0, i) +
                             (Number(afterPoint[i]) + 1) +
                             afterPoint.substr(i+1);
                break;
            }
        }

        finalNumber = beforePoint + '.' + afterPoint;
    }

    // Remove trailing zeroes from fractional part before returning
    return finalNumber.replace(/0+$/, '')
}

Ví dụ sử dụng:

> roundStringNumberWithoutTrailingZeroes(1.6, 2)
'1.6'
> roundStringNumberWithoutTrailingZeroes(10000, 2)
'10000'
> roundStringNumberWithoutTrailingZeroes(0.015, 2)
'0.02'
> roundStringNumberWithoutTrailingZeroes('0.015000', 2)
'0.02'
> roundStringNumberWithoutTrailingZeroes(1, 1)
'1'
> roundStringNumberWithoutTrailingZeroes('0.015', 2)
'0.02'
> roundStringNumberWithoutTrailingZeroes(0.01499999999999999944488848768742172978818416595458984375, 2)
'0.02'
> roundStringNumberWithoutTrailingZeroes('0.01499999999999999944488848768742172978818416595458984375', 2)
'0.01'

Chức năng trên có lẽ là những gì bạn muốn sử dụng để tránh người dùng chứng kiến ​​những con số mà họ đã nhập bị làm tròn sai.

(Để thay thế, bạn cũng có thể thử thư viện round10 cung cấp chức năng tương tự với cách triển khai cực kỳ khác biệt.)

Nhưng điều gì sẽ xảy ra nếu bạn có loại Số thứ hai - một giá trị được lấy từ thang đo liên tục, trong đó không có lý do gì để nghĩ rằng các biểu diễn thập phân gần đúng với số thập phân ít chính xác hơn các số có nhiều số thập phân hơn? Trong trường hợp đó, chúng tôi không muốn tôn trọng biểu diễn Chuỗi, vì biểu diễn đó (như được giải thích trong thông số kỹ thuật) đã được sắp xếp theo thứ tự; chúng tôi không muốn phạm sai lầm khi nói "0,011999999 ... 375 vòng lên tới 0,015, làm tròn đến 0,02, vì vậy 0,011999999 ... 375 vòng lên 0,02".

Ở đây chúng ta chỉ cần sử dụng toFixedphương thức tích hợp sẵn. Lưu ý rằng bằng cách gọi Number()Chuỗi được trả về bởi toFixed, chúng ta sẽ nhận được một Số có biểu diễn Chuỗi không có các số 0 ở cuối (nhờ cách JavaScript tính toán biểu diễn Chuỗi của một Số, đã thảo luận trước đó trong câu trả lời này).

/**
 * Takes a float and rounds it to at most dp decimal places. For example
 *
 *     roundFloatNumberWithoutTrailingZeroes(1.2345, 3)
 *
 * returns 1.234
 *
 * Note that since this treats the value passed to it as a floating point
 * number, it will have counterintuitive results in some cases. For instance,
 * 
 *     roundFloatNumberWithoutTrailingZeroes(0.015, 2)
 *
 * gives 0.01 where 0.02 might be expected. For an explanation of why, see
 * http://stackoverflow.com/a/38676273/1709587. You may want to consider using the
 * roundStringNumberWithoutTrailingZeroes function there instead.
 *
 * @param {number} num
 * @param {number} dp
 * @return {number}
 */
function roundFloatNumberWithoutTrailingZeroes (num, dp) {
    var numToFixedDp = Number(num).toFixed(dp);
    return Number(numToFixedDp);
}

Điều này không hoạt động trong một số trường hợp cạnh: thử ( jsfiddle ) với roundStringNumberWithoutTrailingZeroes(362.42499999999995, 2). Kết quả mong đợi (như trong PHP echo round(362.42499999999995, 2)) : 362.43. Kết quả thực tế:362.42
Bác sĩ Gianluigi Zane Zanettini

1
@ Dr.GianluigiZaneZanettini Huh. Kỳ dị. Tôi không chắc tại sao PHP roundlại cho 362,43. Điều đó có vẻ sai theo trực giác, vì 362.42499999999995 nhỏ hơn 362.425 (về toán học và mã - 362.42499999999995 < 362.425là đúng trong cả JS và PHP). Kể từ đó, câu trả lời của PHP cũng không giảm thiểu khoảng cách giữa các số dấu phẩy động ban đầu và được làm tròn 362.43 - 362.42499999999995 > 362.42499999999995 - 362.42. Theo php.net/manual/en/feft.round.php , PHP roundtuân theo tiêu chuẩn C99; Tôi sẽ phải mạo hiểm đến vùng đất C để hiểu chuyện gì đang xảy ra.
Đánh dấu Amery

1
Sau khi xem xét việc thực hiện làm tròn trong nguồn của PHP , tôi không biết nó đang làm gì. Đó là một triển khai phức tạp khủng khiếp với đầy đủ các macro và các nhánh và chuyển đổi chuỗi và phân nhánh trên các giá trị chính xác ma thuật được mã hóa cứng. Tôi không chắc phải nói gì ngoài "câu trả lời của PHP là hoàn toàn sai và chúng ta nên nộp báo cáo lỗi". Bằng cách nào bạn tìm thấy số 362.42499999999995?
Đánh dấu Amery

1
@ Dr.GianluigiZaneZanettini Tôi đã tạo một báo cáo lỗi: bug.php.net/orms.php?id=75644
Mark Amery

2
@ Dr.GianluigiZaneZanettini "Tôi có ý nghĩa gì không?" - không; đó không phải là cách làm tròn. 1,000005 kết thúc bằng năm, nhưng nếu được làm tròn đến số nguyên gần nhất, câu trả lời phải là 1, không phải 2. Tương tự, 1499995 kết thúc bằng năm, nhưng nếu làm tròn đến một triệu gần nhất, kết quả sẽ là 1000000, không phải 2000000. Trong trường hợp 362.42499999999995 được làm tròn thành 2 DP, điều cần xác định hướng làm tròn là vị trí thập phân thứ ba , là 4.
Đánh dấu Amery

76

Xem xét .toFixed().toPrecision():

http://www.javascriptkit.com/javatutors/formatnumber.shtml


1
toFixed thêm các dấu thập phân vào mọi giá trị bất kể là gì.
stinkycheeseman

13
Cả hai đều vô dụng ở đây
Esailija

Thật không may, cả hai chức năng sẽ thêm các vị trí thập phân bổ sung mà @stinkycheeseman dường như không muốn.
jackwanders


2
chúng trả về các chuỗi và không phải là số, vì vậy chúng đang định dạng và không tính toán ..
sa Elliis_devel

63

Một phương pháp làm tròn chính xác. Nguồn: Mozilla

(function(){

    /**
     * Decimal adjustment of a number.
     *
     * @param   {String}    type    The type of adjustment.
     * @param   {Number}    value   The number.
     * @param   {Integer}   exp     The exponent (the 10 logarithm of the adjustment base).
     * @returns {Number}            The adjusted value.
     */
    function decimalAdjust(type, value, exp) {
        // If the exp is undefined or zero...
        if (typeof exp === 'undefined' || +exp === 0) {
            return Math[type](value);
        }
        value = +value;
        exp = +exp;
        // If the value is not a number or the exp is not an integer...
        if (isNaN(value) || !(typeof exp === 'number' && exp % 1 === 0)) {
            return NaN;
        }
        // Shift
        value = value.toString().split('e');
        value = Math[type](+(value[0] + 'e' + (value[1] ? (+value[1] - exp) : -exp)));
        // Shift back
        value = value.toString().split('e');
        return +(value[0] + 'e' + (value[1] ? (+value[1] + exp) : exp));
    }

    // Decimal round
    if (!Math.round10) {
        Math.round10 = function(value, exp) {
            return decimalAdjust('round', value, exp);
        };
    }
    // Decimal floor
    if (!Math.floor10) {
        Math.floor10 = function(value, exp) {
            return decimalAdjust('floor', value, exp);
        };
    }
    // Decimal ceil
    if (!Math.ceil10) {
        Math.ceil10 = function(value, exp) {
            return decimalAdjust('ceil', value, exp);
        };
    }
})();

Ví dụ:

// Round
Math.round10(55.55, -1); // 55.6
Math.round10(55.549, -1); // 55.5
Math.round10(55, 1); // 60
Math.round10(54.9, 1); // 50
Math.round10(-55.55, -1); // -55.5
Math.round10(-55.551, -1); // -55.6
Math.round10(-55, 1); // -50
Math.round10(-55.1, 1); // -60
Math.round10(1.005, -2); // 1.01 -- compare this with Math.round(1.005*100)/100 above
// Floor
Math.floor10(55.59, -1); // 55.5
Math.floor10(59, 1); // 50
Math.floor10(-55.51, -1); // -55.6
Math.floor10(-51, 1); // -60
// Ceil
Math.ceil10(55.51, -1); // 55.6
Math.ceil10(51, 1); // 60
Math.ceil10(-55.59, -1); // -55.5
Math.ceil10(-59, 1); // -50

Ai đó cũng đưa nó lên GitHub và npm: github.com/jhohlfeld/round10
Jo Liss

Không, Math.round10(3544.5249, -2)trả lại 3544,52 thay vì 3544,53
Matija

2
@Matija Wolfram alpha cũng nói 3544,52. Bạn muốn giảm thiểu lỗi giữa số hiện tại và xấp xỉ làm tròn. Giá trị gần đúng nhất của 3544.52492 chữ số thập phân là 3544.52(error = 0,0049). Nếu có 3544.53, lỗi sẽ là 0,0051. Bạn đang thực hiện làm tròn liên tiếp, ví dụ Math.round10 (Math.round10 (3544.5249, -3), -2) sẽ gây ra lỗi làm tròn lớn hơn và do đó không mong muốn.
người dùng

3
@Matija, theo quan điểm toán học, 3544,5249 làm tròn là 3544,52, không phải 3544,53, vì vậy mã này là chính xác. Nếu bạn muốn làm tròn nó thành 3544,53 trong trường hợp này và các trường hợp như thế này (thậm chí khó khăn là không chính xác), hãy làm một cái gì đó như thế này:number += 0.00011
Bozidar Sikanjic

@Matija: Tôi nghĩ rằng chức năng hoạt động như bình thường. Có thể bạn muốn lặp lại cách làm tròn như:Math.round10( Math.round10(3544.5249, -3) , -2)
jumxozizi

60

Không có câu trả lời nào được tìm thấy ở đây là chính xác . @stinkycheeseman yêu cầu làm tròn , tất cả các bạn làm tròn số.

Để làm tròn, sử dụng:

Math.ceil(num * 100)/100;

15
Ví dụ đầu vào và đầu ra cho thấy rằng mặc dù câu hỏi đã nói 'làm tròn ...' nhưng thực ra nó được dự định là 'làm tròn thành ...'.
JayDM

2
@stinkycheeseman đã chỉ ra một lỗi trong một trường hợp cụ thể, anh ta không muốn luôn luôn làm tròn như trần, anh ta chỉ muốn 0,005 để làm tròn đến 0,01
mjaggard

9
Đã tìm thấy lỗi kỳ lạ trong khi thử nghiệm Math.ceil(1.1 * 100)/100;- nó trả về 1.11, bởi vì 1.1 * 100 là 110.00000000000001theo các trình duyệt hiện đại hoàn toàn mới Firefox, Chrome, Safari và Opera ... IE, theo kiểu cũ, vẫn nghĩ 1.1*100=1100.
skobaljic

1
@skobaljic thửMath.ceil(num.toFixed(4) * 100) / 100
Treeface 11/12/13

1
@treeface Math.ceil((1.1).toFixed(4) * 100) / 100cũng sẽ trở lại 1.11trong Firefox, vấn đề / lỗi trình duyệt hiện đại có sự nhân lên và mọi người nên biết về nó (ví dụ tôi đã làm việc với một trò chơi xổ số).
skobaljic

47

Đây là một cách đơn giản để làm điều đó:

Math.round(value * 100) / 100

Bạn có thể muốn tiếp tục và tạo một chức năng riêng để làm điều đó cho bạn:

function roundToTwo(value) {
    return(Math.round(value * 100) / 100);
}

Sau đó, bạn chỉ cần vượt qua trong giá trị.

Bạn có thể cải thiện nó để làm tròn đến bất kỳ số thập phân tùy ý nào bằng cách thêm một tham số thứ hai.

function myRound(value, places) {
    var multiplier = Math.pow(10, places);

    return (Math.round(value * multiplier) / multiplier);
}

2
Giải pháp này sai, xem stackoverflow.com/questions/38322372/ nếu bạn nhập 156893.145 và làm tròn nó với chức năng trên, bạn nhận được 156893,14 thay vì 156893.15 !!!
sa Elliis_devel

2
@sa Elliis_devel, bạn đã quản lý để tìm một trường hợp trong đó việc biểu diễn số của các số trong JavaScript không thành công. Bạn đúng rằng nó làm tròn không chính xác. Nhưng - đó là vì số mẫu của bạn khi nhân với 100 đã bị hỏng (15689314.499999998). Thực sự, một câu trả lời đầy đủ tính năng sẽ yêu cầu một thư viện đã được phát triển cụ thể để tính đến sự không nhất quán trong việc xử lý số thực của JavaScript. Nếu không, bạn có thể làm mất hiệu lực bất kỳ câu trả lời nào đã được đưa ra cho câu hỏi này.
JayDM

36
+(10).toFixed(2); // = 10
+(10.12345).toFixed(2); // = 10.12

(10).toFixed(2); // = 10.00
(10.12345).toFixed(2); // = 10.12

1
Điều này sẽ không luôn cho kết quả giống như bạn nhận được nếu bạn lấy đại diện Chuỗi của Số của bạn và làm tròn nó. Ví dụ , +(0.015).toFixed(2) == 0.01.
Đánh dấu Amery

35

Đối với tôi Math.round () đã không đưa ra câu trả lời đúng. Tôi thấy toFixed (2) hoạt động tốt hơn. Dưới đây là ví dụ của cả hai:

console.log(Math.round(43000 / 80000) * 100); // wrong answer

console.log(((43000 / 80000) * 100).toFixed(2)); // correct answer


Điều quan trọng cần lưu ý là toFixed không thực hiện làm tròn và Math.round chỉ làm tròn đến số nguyên gần nhất. Do đó, để bảo toàn số thập phân, chúng ta cần nhân số gốc với số lũy thừa của mười số có số 0 biểu thị số thập phân mong muốn của bạn, sau đó chia kết quả cho cùng một số. Trong trường hợp của bạn: Math.round (43000/80000 * 100 * 100) / 100. Cuối cùng, có thể áp dụng để kết hợp (2) để đảm bảo luôn có hai số thập phân trong kết quả (với các số 0 ở cuối khi cần) - hoàn hảo để sắp xếp đúng một dãy số được trình bày theo chiều dọc :)
Turbo

7
Cũng cần lưu ý rằng .toFIxed () xuất ra một chuỗi chứ không phải một số.
Carpiediem

2
Điều này vẫn không giải quyết được làm tròn cho 1.005. (1.005).toFixed(2)vẫn kết quả đến 1.00.
DPac

34

Sử dụng chức năng này Number(x).toFixed(2);


8
Gói lại tất cả Number, nếu bạn không muốn nó được trả về dưới dạng chuỗi:Number(Number(x).toFixed(2));

5
Cuộc Numbergọi không cần thiết, x.toFixed(2)hoạt động.
bgusach

3
@bgusach Số cuộc gọi cần thiết, vì câu lệnh x.toFixed (2) trả về chuỗi và không phải là số. Để chuyển đổi một lần nữa thành số, chúng ta cần kết hợp với Số
Mohan Ram

2
Khi sử dụng phương thức này (1).toFixed(2)trả về 1.00, nhưng người hỏi cần thiết 1trong trường hợp này.
Eugene Mala

1
Điều này không hoạt động, 1.005.toFixed(2)mang lại "1"khi cần "1.01".
Adam Jagosz

33

2017
Chỉ cần sử dụng mã gốc.toFixed()

number = 1.2345;
number.toFixed(2) // "1.23"

Nếu bạn cần nghiêm ngặt và thêm chữ số nếu cần, nó có thể sử dụng replace

number = 1; // "1"
number.toFixed(5).replace(/\.?0*$/g,'');

3
Phương thức toFixed trả về một chuỗi. Nếu bạn muốn một kết quả số, bạn sẽ cần gửi kết quả của toFixed tới parseFloat.
Zambonilli

@Zambonilli Hoặc chỉ cần nhân 1 nếu cần thiết. nhưng vì số lượng cố định nên hầu hết các trường hợp là để hiển thị và không phải chuỗi tính toán là định dạng đúng
pery mimon

2
-1; không chỉ được toFixedđề xuất bởi nhiều câu trả lời nhiều năm trước bạn, mà còn không thỏa mãn điều kiện "chỉ khi cần thiết" trong câu hỏi; (1).toFixed(2)đưa ra "1.00"nơi người hỏi mong muốn "1".
Đánh dấu Amery

OK đã nhận nó. Tôi cũng thêm một số giải pháp cho trường hợp đó
pery mimon

Nếu bạn đang sử dụng lodash, điều đó thậm chí còn dễ dàng hơn: _.round (number, binaryPlace) Đã xóa nhận xét cuối cùng của tôi, vì nó có vấn đề. Mặc dù vậy, Lodash _.round DOES hoạt động. 1,005 với vị trí thập phân của 2 chuyển đổi thành 1,01.
Trường Devin

32

Hãy thử giải pháp trọng lượng nhẹ này :

function round(x, digits){
  return parseFloat(x.toFixed(digits))
}

 round(1.222,  2) ;
 // 1.22
 round(1.222, 10) ;
 // 1.222

Bất cứ ai cũng biết nếu có bất kỳ sự khác biệt giữa điều này và return Number(x.toFixed(digits))?

1
@JoeRocc ... sẽ không tạo ra sự khác biệt xa như tôi có thể thấy vì .toFixed()chỉ cho phép đối với các số.
petermeissner

4
Câu trả lời này có cùng một vấn đề như được đề cập nhiều lần trên trang này. Hãy thử round(1.005, 2)và xem một kết quả 1thay vì 1.01.
MilConDoin

dường như nhiều vấn đề của thuật toán làm tròn? - có nhiều hơn một người sẽ tưởng tượng: en.wikipedia.org/wiki/Rounding ... round(0.995, 2) => 0.99; round(1.006, 2) => 1.01; round(1.005, 2) => 1
petermeissner

31

Có một vài cách để làm điều đó. Đối với những người như tôi, biến thể của Lodash

function round(number, precision) {
    var pair = (number + 'e').split('e')
    var value = Math.round(pair[0] + 'e' + (+pair[1] + precision))
    pair = (value + 'e').split('e')
    return +(pair[0] + 'e' + (+pair[1] - precision))
}

Sử dụng:

round(0.015, 2) // 0.02
round(1.005, 2) // 1.01

Nếu dự án của bạn sử dụng jQuery hoặc lodash, bạn cũng có thể tìm thấy roundphương thức thích hợp trong các thư viện.

Cập nhật 1

Tôi loại bỏ các biến thể n.toFixed(2), bởi vì nó không chính xác. Cảm ơn bạn @ avalanche1


Tùy chọn thứ hai sẽ trả về một chuỗi có chính xác hai điểm thập phân. Câu hỏi chỉ yêu cầu điểm thập phân nếu cần thiết. Tùy chọn đầu tiên là tốt hơn trong trường hợp này.
Marcos Lima

@MarcosLima Number.toFixed()sẽ trả về một chuỗi nhưng với ký hiệu dấu cộng trước nó, trình thông dịch JS sẽ chuyển đổi chuỗi thành một số. Đây là một cú pháp đường.
stanleyxu2005

Trên Firefox, alert((+1234).toFixed(2))hiển thị "1234.00".
Marcos Lima

Trên Firefox, alert(+1234.toFixed(2))ném SyntaxError: identifier starts immediately after numeric literal. Tôi gắn bó với tùy chọn 1.
Marcos Lima

Điều này không hoạt động trong một số trường hợp cạnh: thử ( jsfiddle ) với 362.42499999999995. Kết quả mong đợi (như trong PHP echo round(362.42499999999995, 2)) : 362.43. Kết quả thực tế:362.42
Bác sĩ Gianluigi Zane Zanettini

26

Nếu bạn đang sử dụng thư viện lodash, bạn có thể sử dụng phương pháp vòng lodash như sau.

_.round(number, precision)

Ví dụ:

_.round(1.7777777, 2) = 1.78

@Peter Tập hợp các chức năng mà Lodash cung cấp thực sự tốt so với Javascript tiêu chuẩn. Tuy nhiên, tôi nghe nói rằng Lodash có một số vấn đề về hiệu năng so với tiêu chuẩn JS. codeburst.io/ Kẻ
Madura Pradeep

1
Tôi chấp nhận quan điểm của bạn rằng có những hạn chế về hiệu suất khi sử dụng lodash. Tôi nghĩ rằng những vấn đề đó là phổ biến cho nhiều trừu tượng. Nhưng chỉ cần nhìn vào có bao nhiêu câu trả lời trên chủ đề này và làm thế nào các giải pháp trực quan thất bại cho các trường hợp cạnh. Chúng tôi đã thấy mô hình này với jQuery và vấn đề gốc đã được giải quyết khi các trình duyệt áp dụng một tiêu chuẩn chung giải quyết hầu hết các trường hợp sử dụng của chúng tôi. Các nút thắt hiệu suất sau đó đã được chuyển sang các công cụ trình duyệt. Tôi nghĩ điều tương tự sẽ xảy ra với lodash. :)
Peter

26

Vì ES6 có một cách 'phù hợp' (không ghi đè thống kê và tạo cách giải quyết) để thực hiện điều này bằng cách sử dụng toPrecision

var x = 1.49999999999;
console.log(x.toPrecision(4));
console.log(x.toPrecision(3));
console.log(x.toPrecision(2));

var y = Math.PI;
console.log(y.toPrecision(6));
console.log(y.toPrecision(5));
console.log(y.toPrecision(4));

var z = 222.987654
console.log(z.toPrecision(6));
console.log(z.toPrecision(5));
console.log(z.toPrecision(4));

sau đó bạn có thể chỉ parseFloatvà số không sẽ 'biến mất'.

console.log(parseFloat((1.4999).toPrecision(3)));
console.log(parseFloat((1.005).toPrecision(3)));
console.log(parseFloat((1.0051).toPrecision(3)));

Mặc dù vậy, nó không giải quyết được '1.005 vấn đề làm tròn' - vì nó thực chất là cách các phân số float đang được xử lý .

console.log(1.005 - 0.005);

Nếu bạn mở thư viện, bạn có thể sử dụng bignumber.js

console.log(1.005 - 0.005);
console.log(new BigNumber(1.005).minus(0.005));

console.log(new BigNumber(1.005).round(4));
console.log(new BigNumber(1.005).round(3));
console.log(new BigNumber(1.005).round(2));
console.log(new BigNumber(1.005).round(1));
<script src="https://cdnjs.cloudflare.com/ajax/libs/bignumber.js/2.3.0/bignumber.min.js"></script>


3
(1.005).toPrecision(3)vẫn trả về 1.00thay vì 1.01thực sự.
Giacomo

toPrecisiontrả về một chuỗi thay đổi loại đầu ra mong muốn.
adamduren

@Giacomo Nó không phải là một lỗ hổng của .toPrecisionphương thức, nó là một đặc trưng của các số dấu phẩy động (mà các số trong JS là) - hãy thử 1.005 - 0.005, nó sẽ trả về 0.9999999999999999.
shau-kote

1
(1).toPrecision(3)trả về '1,00', nhưng người hỏi muốn có 1trong trường hợp này.
Eugene Mala

1
Như @Giacomo đã nói, câu trả lời này dường như nhầm lẫn giữa "các chữ số có nghĩa" với "làm tròn đến một số vị trí thập phân". toPrecisionđịnh dạng này, không phải định dạng sau và không phải là câu trả lời cho câu hỏi của OP, mặc dù ban đầu có vẻ như nó có liên quan nhưng nó lại sai rất nhiều. Xem en.wikipedia.org/wiki/Significant_f configure . Ví dụ Number(123.4).toPrecision(2)trả lại "1.2e+2"Number(12.345).toPrecision(2)trả lại "12". Tôi cũng đồng ý với quan điểm của @ adamduren rằng nó trả về một chuỗi không mong muốn (không phải là vấn đề lớn nhưng không được mong muốn).
Neek

23

MarkG và Lavamantis đưa ra một giải pháp tốt hơn nhiều so với giải pháp đã được chấp nhận. Thật xấu hổ khi họ không nhận được nhiều sự ủng hộ hơn!

Đây là chức năng tôi sử dụng để giải quyết các vấn đề về số thập phân dấu phẩy động cũng dựa trên MDN . Nó thậm chí còn chung chung hơn (nhưng ít súc tích hơn) so với giải pháp của Lavamantis:

function round(value, exp) {
  if (typeof exp === 'undefined' || +exp === 0)
    return Math.round(value);

  value = +value;
  exp  = +exp;

  if (isNaN(value) || !(typeof exp === 'number' && exp % 1 === 0))
    return NaN;

  // Shift
  value = value.toString().split('e');
  value = Math.round(+(value[0] + 'e' + (value[1] ? (+value[1] + exp) : exp)));

  // Shift back
  value = value.toString().split('e');
  return +(value[0] + 'e' + (value[1] ? (+value[1] - exp) : -exp));
}

Sử dụng nó với:

round(10.8034, 2);      // Returns 10.8
round(1.275, 2);        // Returns 1.28
round(1.27499, 2);      // Returns 1.27
round(1.2345678e+2, 2); // Returns 123.46

So với giải pháp của Lavamantis, chúng ta có thể ...

round(1234.5678, -2); // Returns 1200
round("123.45");      // Returns 123

2
Giải pháp của bạn không bao gồm một số trường hợp trái ngược với giải pháp của MDN. Mặc dù nó có thể ngắn hơn, nhưng nó không chính xác ...
astorije

1
vòng (-1835.665,2) => -1835.66
Jorge

21

Điều này có thể giúp bạn:

var result = Math.round(input*100)/100;

để biết thêm thông tin, bạn có thể xem liên kết này

Math.round (num) so với num.toFixed (0) và sự không nhất quán của trình duyệt


1
Tại sao trên thế giới, câu trả lời được chấp nhận lại có nhiều phiếu hơn so với câu này vì thực tế chúng giống nhau, nhưng câu trả lời này được đăng 1 phút sau khi được chấp nhận?
Trích dẫn Dave

18

Cách tiếp cận đơn giản nhất là sử dụng toFixed và sau đó tước các số 0 ở cuối bằng cách sử dụng hàm Number:

const number = 15.5;
Number(number.toFixed(2)); // 15.5
const number = 1.7777777;
Number(number.toFixed(2)); // 1.78

Điều này không làm việc cho tất cả các trường hợp. làm các bài kiểm tra mở rộng trước khi đăng câu trả lời.
baburao

@baburao Vui lòng đăng một trường hợp trong đó giải pháp trên không hoạt động
Marcin Wanago

số const = 15; Số (số.toFixed (2)); // 15.00 thay vì 15
Kevin Jhangiani

1
@KevinJhangiani const số = 15; Số (số.toFixed (2)); // 15 - Tôi đã thử nghiệm cả trên Chrome và Firefox mới nhất
Marcin Wanago

@KevinJhangiani làm thế nào để bạn có được 15.00? Các số trong JS không lưu trữ các vị trí thập phân và bất kỳ màn hình nào sẽ tự động cắt bớt các vị trí thập phân thừa (bất kỳ số 0 nào ở cuối).
VLAZ

16
var roundUpto = function(number, upto){
    return Number(number.toFixed(upto));
}
roundUpto(0.1464676, 2);

toFixed(2) ở đây 2 là số chữ số mà chúng tôi muốn làm tròn số này.


Điều này .toFixed () đơn giản hơn để thực hiện. chỉ cần đi qua nó một lần
Ritesh Dhuri


14

Cách dễ nhất:

+num.toFixed(2)

Nó chuyển đổi nó thành một chuỗi, và sau đó trở lại thành một số nguyên / float.


Cảm ơn câu trả lời đơn giản nhất này. Tuy nhiên, '+' trong + num là gì? Nó không hoạt động đối với tôi khi chuỗi thập phân xuất hiện trong chuỗi. Tôi đã làm: (num * 1) .toFixed (2).
Ethan

@momo chỉ cần thay đổi đối số toFixed()thành 3. Vì vậy, nó sẽ được +num.toFixed(3). Điều đó hoạt động theo cách nó được yêu cầu, 1.005 được làm tròn thành 1, tương đương với 1
bigpotato

1
@Edmund Dự kiến ​​sẽ trả lại 1,01, không phải 1,00
mmm

13

Đây là một phương pháp nguyên mẫu:

Number.prototype.round = function(places){
    places = Math.pow(10, places); 
    return Math.round(this * places)/places;
}

var yournum = 10.55555;
yournum = yournum.round(2);

13

Sử dụng cái gì đó như "parseFloat (parseFloat (value) .toFixed (2))"

parseFloat(parseFloat("1.7777777").toFixed(2))-->1.78 
parseFloat(parseFloat("10").toFixed(2))-->10 
parseFloat(parseFloat("9.1").toFixed(2))-->9.1

1
không nếu sự không chính xác là nội tại của đại diện float. bạn sẽ chỉ cần loại bỏ nó và sau đó giới thiệu lại cùng một lỗi bằng cách chuyển đổi trở lại để nổi một lần nữa!
Ben McIntyre

12

Một cách để đạt được làm tròn như vậy chỉ khi cần thiết là sử dụng Number.prototype.toLocaleString () :

myNumber.toLocaleString('en', {maximumFractionDigits:2, useGrouping:false})

Điều này sẽ cung cấp chính xác đầu ra mà bạn mong đợi, nhưng dưới dạng chuỗi. Bạn vẫn có thể chuyển đổi các số đó thành số nếu đó không phải là loại dữ liệu bạn mong đợi.


Đây là giải pháp sạch nhất từ ​​trước đến nay và vượt qua tất cả các vấn đề về dấu phẩy động phức tạp, nhưng mỗi hỗ trợ MDN vẫn chưa hoàn tất - Safari chưa hỗ trợ chuyển các đối số toLocaleString.
Đánh dấu Amery

@MarkAmery Hiện tại, chỉ có Trình duyệt Android có một số vấn đề: caniuse.com/#search=toLocaleString
ptyskju

12

Sau khi chạy qua nhiều lần lặp khác nhau của tất cả các cách có thể để đạt được độ chính xác làm tròn thập phân chính xác, rõ ràng giải pháp chính xác và hiệu quả nhất là sử dụng Number.EPSILON. Điều này cung cấp một giải pháp toán học thực sự cho vấn đề chính xác toán học dấu phẩy động. Nó có thể dễ dàng được điền vào như được hiển thị ở đây: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/EPSILON để hỗ trợ tất cả những người dùng IE còn lại cuối cùng nên ngừng làm điều đó).

Chuyển thể từ giải pháp được cung cấp tại đây: https://stackoverflow.com/a/48850944/6910392

Một giải pháp đơn giản cung cấp làm tròn thập phân, sàn và trần chính xác, với một biến chính xác tùy chọn mà không cần thêm toàn bộ thư viện.

CẬP NHẬT: Như Serge đã lưu ý trong các bình luận, có một hạn chế đối với phương pháp này (hoặc bất kỳ) nào đáng để chỉ ra. Trong trường hợp các số như 0,0119999999999999999, bạn vẫn sẽ gặp phải sự không chính xác, là kết quả của việc đạt đến giới hạn tuyệt đối về giới hạn độ chính xác để lưu trữ giá trị dấu phẩy động. Không có toán học hoặc giải pháp nào khác có thể được áp dụng cho tài khoản đó, vì giá trị ngay lập tức được đánh giá là 0,015. Bạn có thể xác nhận điều này bằng cách đơn giản gọi giá trị đó trong bảng điều khiển. Do hạn chế này, thậm chí không thể sử dụng thao tác chuỗi để giảm giá trị này, vì biểu diễn chuỗi của nó chỉ đơn giản là "0,015". Bất kỳ giải pháp nào cho việc này sẽ cần được áp dụng một cách hợp lý tại nguồn dữ liệu trước khi chấp nhận giá trị thành tập lệnh,

var DecimalPrecision = (function(){
        if (Number.EPSILON === undefined) {
            Number.EPSILON = Math.pow(2, -52);
        }
        this.round = function(n, p=2){
            let r = 0.5 * Number.EPSILON * n;
            let o = 1; while(p-- > 0) o *= 10;
            if(n < 0)
                o *= -1;
            return Math.round((n + r) * o) / o;
        }
        this.ceil = function(n, p=2){
            let r = 0.5 * Number.EPSILON * n;
            let o = 1; while(p-- > 0) o *= 10;
            if(n < 0)
                o *= -1;
            return Math.ceil((n + r) * o) / o;
        }
        this.floor = function(n, p=2){
            let r = 0.5 * Number.EPSILON * n;
            let o = 1; while(p-- > 0) o *= 10;
            if(n < 0)
                o *= -1;
            return Math.floor((n + r) * o) / o;
        }
        return this;
    })();
    console.log(DecimalPrecision.round(1.005));
    console.log(DecimalPrecision.ceil(1.005));
    console.log(DecimalPrecision.floor(1.005));
    console.log(DecimalPrecision.round(1.0049999));
    console.log(DecimalPrecision.ceil(1.0049999));
    console.log(DecimalPrecision.floor(1.0049999));
    console.log(DecimalPrecision.round(2.175495134384,7));
    console.log(DecimalPrecision.round(2.1753543549,8));
    console.log(DecimalPrecision.round(2.1755465135353,4));


1
(DecimalPrecision.round (0.014999999999999999, 2)) // trả về 0,02
Sergey

Nắm bắt tốt! Vấn đề là với lưu trữ dấu phẩy động trong JS, luôn luôn có một số trường hợp cạnh. Tin tốt là toán học mà bạn áp dụng cho Number.EPSILON trước tiên có thể được điều chỉnh tốt hơn để đẩy các trường hợp cạnh đó ra xa hơn trên cạnh. Nếu bạn muốn đảm bảo không có khả năng cho các trường hợp cạnh, giải pháp thực sự duy nhất của bạn sẽ là thao tác chuỗi, và sau đó là toán học. Thời điểm bạn thực hiện bất kỳ tính toán toán học nào về giá trị (ngay cả khi cố gắng di chuyển số thập phân), thì bạn đã tạo ra lỗi.
KFish

Trên thực tế, khi kiểm tra thêm, điều này không phải do bất kỳ toán học nào liên quan, mà là vấn đề biểu hiện ngay lập tức khi gọi giá trị được chỉ định. Bạn có thể xác nhận điều này một cách đơn giản bằng cách nhập số đó vào bảng điều khiển và thấy rằng nó ngay lập tức ước tính thành 0,015. Do đó, điều này sẽ đại diện cho độ chính xác tuyệt đối cho bất kỳ số dấu phẩy động nào trong JS. Trong trường hợp này, bạn thậm chí không thể chuyển đổi thành chuỗi và thao tác vì giá trị chuỗi sẽ là "0,015"
KFish

11

Đây là giải pháp đơn giản nhất, thanh lịch hơn (và tôi là người giỏi nhất thế giới;):

function roundToX(num, X) {    
    return +(Math.round(num + "e+"+X)  + "e-"+X);
}
//roundToX(66.66666666,2) => 66.67
//roundToX(10,2) => 10
//roundToX(10.904,2) => 10.9

4
Đó là một cách hay để viết lại câu trả lời được chấp nhận để chấp nhận một đối số bằng cách sử dụng Eký hiệu.
AxelH

1
Điều này không hoạt động trong một số trường hợp cạnh: thử ( jsfiddle ) roundToX(362.42499999999995, 2). Kết quả mong đợi (như trong PHP echo round(362.42499999999995, 2)) : 362.43. Kết quả thực tế:362.42
Tiến sĩ Gianluigi Zane Zanettini

6
IMHO, kết quả PHP của bạn là sai. Không có vấn đề gì xảy ra sau số thập phân thứ ba, nếu số thập phân thứ ba thấp hơn 5, thì số thập phân thứ hai sẽ giữ nguyên. Đó là định nghĩa toán học.
Soldeplata Saketos

1
Để thậm chí ngắn gọn hơn "e +" chỉ có thể là "e".
Lonnie hay nhất
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.