I'd seperti untuk putaran paling banyak 2 tempat desimal, tetapi hanya jika diperlukan.
Input:
10
1.7777777
9.1
Output:
10
1.78
9.1
Bagaimana saya bisa melakukan ini di JavaScript?
Jika nilai adalah jenis teks:
parseFloat("123.456").toFixed(2);
Jika nilai adalah nomor:
var numb = 123.23454;
numb = numb.toFixed(2);
Ada sisi negatifnya bahwa nilai-nilai seperti 1.5 akan memberikan "1.50" sebagai output. Perbaikan yang disarankan oleh @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.
Sepertinya Matematika.bulat
adalah solusi yang lebih baik. Tapi itu tidak! Dalam beberapa kasus itu akan TIDAK **** putaran dengan benar:
Math.round(1.005 * 1000)/1000 // Returns 1 instead of expected 1.01!
toFixed() juga akan TIDAK bulat dengan benar dalam beberapa kasus (diuji di Chrome v. 55.0.2883.87)!
Contoh:
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.
Saya kira, ini karena 1.555 adalah benar-benar sesuatu seperti mengapung 1.55499994 di belakang layar.
Solusi 1 adalah dengan menggunakan script dengan yang dibutuhkan algoritma pembulatan, misalnya:
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
Solusi 2 adalah untuk menghindari front end perhitungan dan menarik nilai bulat dari backend server.
Anda dapat menggunakan
function roundToTwo(num) {
return +(Math.round(num + "e+2") + "e-2");
}
Saya menemukan ini di atas pada MDN. Cara mereka menghindari masalah dengan 1.005 yang sebutkan.
roundToTwo(1.005)
1.01
roundToTwo(10)
10
roundToTwo(1.7777777)
1.78
roundToTwo(9.1)
9.1
roundToTwo(1234.5678)
1234.57
MarkG's jawaban yang benar. Berikut ini's generik ekstensi untuk setiap jumlah tempat desimal.
Number.prototype.round = function(places) {
return +(Math.round(this + "e+" + places) + "e-" + places);
}
Penggunaan:
var n = 1.7777;
n.round(2); // 1.78
Unit test:
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');
});
})
Anda harus menggunakan:
Math.round( num * 100 + Number.EPSILON ) / 100
Tidak ada yang tampaknya menyadari Nomor.EPSILON
.
Juga's dicatat bahwa ini bukan JavaScript keanehan seperti beberapa orang menyatakan.
Itu hanya cara angka floating point bekerja di komputer. Seperti 99% bahasa pemrograman, JavaScript doesn't memiliki home made angka floating point; hal ini bergantung pada CPU/FPU untuk itu. Menggunakan komputer biner dan biner, ada isn't setiap nomor seperti 0.1
, tapi hanya biner pendekatan untuk itu. Mengapa? Untuk alasan yang sama dari 1/3 tidak dapat dituliskan dalam desimal: nilainya 0.33333333... dengan infinity bertiga.
Di sini datang Nomor.EPSILON
. Jumlah itu merupakan selisih antara 1 dan berikutnya nomor ada di double precision floating point nomor. Yang's ini: Ada tidak ada jumlah antara 1
dan 1 + Nomor.EPSILON
.
EDIT:
Seperti yang ditanyakan di komentar, let's menjelaskan satu hal: menambahkan Nomor.EPSILON
hanya relevan ketika nilai untuk putaran adalah hasil dari operasi aritmatika, karena dapat menelan beberapa floating point kesalahan delta.
It's tidak berguna ketika nilai berasal dari sumber langsung (misalnya: literal, input pengguna atau sensor).
EDIT (2019):
Seperti @maganap dan beberapa orang telah menunjukkan, itu's terbaik untuk menambahkan Nomor.EPSILON
sebelum mengalikan:
Math.round( ( num + Number.EPSILON ) * 100 ) / 100
Pertanyaan ini lebih rumit.
Misalkan kita memiliki sebuah fungsi, roundTo2DP(num)
, yang mengambil pelampung sebagai argumen dan mengembalikan nilai dibulatkan sampai 2 tempat desimal. Apa yang harus masing-masing ekspresi yang mengevaluasi ke?
roundTo2DP(0.014999999999999999)
roundTo2DP(0.0150000000000000001)
roundTo2DP(0.015)
The 'jelas' jawabannya adalah bahwa contoh pertama harus bulat untuk 0.01 (karena itu's lebih dekat dari 0.01 ke 0.02) sementara dua lainnya harus bulat untuk 0.02 (karena 0.0150000000000000001 lebih dekat ke 0.02 dari 0,01, dan karena 0.015 adalah persis di tengah antara mereka dan ada matematika konvensi bahwa angka tersebut mendapatkan dibulatkan).
Menangkap, yang anda mungkin telah menebak, adalah bahwa roundTo2DP
tidak bisa dilaksanakan untuk memberikan mereka jawaban yang jelas, karena semua tiga angka yang diberikan adalah nomor yang sama. IEEE 754 biner angka floating point (jenis yang digunakan oleh JavaScript) dapat't persis mewakili sebagian besar non-integer angka, dan semua tiga literal numerik di atas bisa dibulatkan ke terdekat valid floating point nomor. Jumlah ini, seperti yang terjadi, persis
0.01499999999999999944488848768742172978818416595458984375
yang lebih dekat dari 0.01 ke 0.02.
Anda dapat melihat bahwa semua tiga nomor yang sama di konsol browser anda, Node shell, atau JavaScript penerjemah. Bandingkan saja mereka: > 0.014999999999999999 === 0.0150000000000000001
benar
Jadi ketika saya menulis `m = 0.0150000000000000001`, yang *nilai yang tepat dari `m`* yang aku end up dengan lebih dekat ke `0.01` daripada `0.02`. Namun, jika saya convert `m` untuk String...
> var m = 0.0150000000000000001;
> console.log(String(m));
0.015
> var m = 0.014999999999999999;
> console.log(String(m));
0.015
... Saya mendapatkan 0.015, yang harus bulat untuk 0.02, dan yang jelas *tidak* 56 desimal-tempat nomor saya sebelumnya mengatakan bahwa semua angka-angka ini persis sama dengan. Jadi apa sihir gelap ini?
Jawabannya dapat ditemukan dalam ECMAScript spesifikasi, di bagian [*7.1.12.1: ToString diterapkan untuk Jumlah jenis*](http://www.ecma-international.org/ecma-262/7.0/#sec-tostring-applied-to-the-number-type). Berikut ini aturan untuk mengkonversi beberapa Nomor *m* untuk String yang ditetapkan. Bagian penting adalah point 5, di mana integer *s* dihasilkan dan angka yang akan digunakan dalam representasi String dari *m*:
> biarkan *n*, *k*, dan *s* menjadi bilangan bulat sedemikian sehingga *k* ≥ 1, 10k-1 ≤ *s* < 10k, Jumlah nilai *s* × 10n-k adalah *m*, dan *k* adalah sekecil mungkin. Perhatikan bahwa k adalah jumlah digit dalam representasi desimal *s*, yang *s* tidak habis dibagi dengan 10, dan yang paling signifikan digit *s* tidak selalu unik ditentukan oleh kriteria-kriteria tersebut.
Bagian penting di sini adalah persyaratan yang "*k* adalah sekecil mungkin". Apa persyaratan bahwa jumlah ini adalah persyaratan yang, diberi Nomor `m`, nilai `String(m)` harus memiliki *yang paling mungkin jumlah digit* sementara masih memenuhi persyaratan bahwa `Jumlah(String(m)) === m`. Karena kita sudah tahu bahwa `0.015 === 0.0150000000000000001`, it's sekarang jelas mengapa `String(0.0150000000000000001) === '0.015'` harus benar.
Tentu saja, tidak ada pembahasan ini telah dijawab langsung apa `roundTo2DP(m)` *harus* kembali. Jika `m`'s nilai yang tepat adalah 0.01499999999999999944488848768742172978818416595458984375, tetapi representasi String adalah '0.015', lalu apa *benar* menjawab secara matematis, praktis, filosofis, atau apa pun - ketika kita putaran ke dua tempat desimal?
Tidak ada satu jawaban yang benar untuk ini. Hal ini tergantung pada kasus penggunaan anda. Anda mungkin ingin menghormati representasi String dan bulat ke atas ketika:
/**
* 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+$/, '')
}
Contoh penggunaan:
> 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)& lt;/i>
'0.01'
Fungsi di atas adalah *mungkin* apa yang ingin anda gunakan untuk menghindari pengguna yang pernah menyaksikan angka-angka yang mereka telah masuk menjadi bulat salah.
(Sebagai alternatif, anda bisa juga mencoba [round10](https://github.com/jhohlfeld/round10) perpustakaan yang menyediakan sama-berperilaku fungsi dengan liar implementasi yang berbeda.)
Tetapi bagaimana jika anda memiliki kedua jenis Nomor - nilai yang diambil dari terus menerus skala, di mana ada's tidak ada alasan untuk berpikir bahwa perkiraan representasi desimal dengan lebih sedikit desimal yang lebih akurat ** dari orang-orang dengan lebih banyak? Dalam hal ini, kita *don't* ingin menghormati representasi String, karena representasi itu (seperti yang dijelaskan di spec) sudah sort-of-bulat; kami don't ingin membuat kesalahan dengan mengatakan "0.014999999...375 putaran sampai 0.015, yang putaran hingga 0.02, jadi 0.014999999...375 putaran sampai 0.02".
Di sini kita hanya dapat menggunakan built-in [`toFixed`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed) metode. Perhatikan bahwa dengan memanggil `Nomor()` pada String yang dikembalikan oleh `toFixed`, kita mendapatkan Jumlah dan representasi String tidak memiliki ekor angka nol (terima kasih untuk jalan JavaScript menghitung representasi String dari Nomor, yang dibahas sebelumnya dalam jawaban ini).
/**
* 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);
}
Tepat metode pembulatan. Sumber: 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);
};
}
})();
Contoh:
// 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
Berikut ini adalah cara sederhana untuk melakukannya:
Math.round(value * 100) / 100
Anda mungkin ingin pergi ke depan dan membuat fungsi yang terpisah untuk melakukannya untuk anda meskipun:
function roundToTwo(value) {
return(Math.round(value * 100) / 100);
}
Kemudian anda hanya akan lulus dalam nilai.
Anda bisa meningkatkan ke putaran untuk setiap sewenang-wenang jumlah desimal dengan menambahkan parameter kedua.
function myRound(value, places) {
var multiplier = Math.pow(10, places);
return (Math.round(value * multiplier) / multiplier);
}
Bagi saya Matematika.round() tidak memberikan jawaban yang benar. Saya menemukan toFixed(2) karya-karya yang lebih baik. Di bawah ini adalah contoh kedua:
console.log(Math.round(43000 / 80000) * 100); // wrong answer
console.log(((43000 / 80000) * 100).toFixed(2)); // correct answer
Gunakan fungsi ini Nomor(x).toFixed(2);
Dua ribu tujuh belas
Hanya menggunakan kode asli .toFixed()
number = 1.2345;
number.toFixed(2) // "1.23"
Jika anda perlu untuk menjadi ketat dan tambahkan digit hanya jika diperlukan dapat menggunakan menggantikan
number = 1; // "1"
number.toFixed(5).replace(/\.?0*$/g,'');
Ada beberapa cara untuk melakukan itu. Untuk orang-orang seperti saya, Lodash's varian
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))
}
Penggunaan:
round(0.015, 2) // 0.02
round(1.005, 2) // 1.01
Jika proyek anda menggunakan jQuery atau lodash, anda juga bisa menemukan yang tepat bulat
metode di perpustakaan.
Saya dihapus varian n.toFixed(2)
, karena itu tidak benar. Terima kasih @avalanche1
MarkG dan Lavamantis ditawarkan sebuah solusi yang jauh lebih baik daripada yang telah diterima. It's rasa malu mereka don't mendapatkan lebih banyak upvotes!
Berikut adalah fungsi yang saya gunakan untuk memecahkan floating point desimal masalah juga didasarkan pada MDN. Hal ini bahkan lebih generik (tapi kurang ringkas) dari Lavamantis's solusi:
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));
}
Menggunakannya dengan:
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
Dibandingkan dengan Lavamantis's solusi, yang bisa kita lakukan...
round(1234.5678, -2); // Returns 1200
round("123.45"); // Returns 123
Sejak ES6 ada 'tepat' cara (tanpa mengesampingkan statika dan menciptakan workarounds) untuk melakukan hal ini dengan menggunakan 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));
maka anda hanya dapat parseFloat
dan nol akan 'pergi'.
konsol.log(parseFloat((1.4999).toPrecision(3))); konsol.log(parseFloat((1.005).toPrecision(3))); konsol.log(parseFloat((1.0051).toPrecision(3)));