Saya ingin membulatkan paling banyak 2 tempat desimal, tetapi hanya jika perlu .
Memasukkan:
10
1.7777777
9.1
Keluaran:
10
1.78
9.1
Bagaimana saya bisa melakukan ini dalam JavaScript?
javascript
rounding
decimal-point
stinkycheeseman
sumber
sumber
Number.EPSILON
. GunakanMath.round( num * 100 + Number.EPSILON ) / 100
.Number.EPSILON
sini?Jawaban:
Menggunakan
Math.round(num * 100) / 100
Sunting: untuk memastikan hal-hal seperti putaran 1.005 dengan benar, kami menggunakan
Math.round((num + Number.EPSILON) * 100) / 100
sumber
Math.round((num + 0.00001) * 100) / 100
. CobaMath.round((1.005 + 0.00001) * 100) / 100
danMath.round((1.0049 + 0.00001) * 100) / 100
Jika nilainya adalah jenis teks:
Jika nilainya adalah angka:
Ada downside yang nilai-nilai seperti 1,5 akan memberikan "1,50" sebagai output. Perbaikan yang disarankan oleh @minitech:
Sepertinya
Math.round
ini solusi yang lebih baik. Tapi ternyata tidak! Dalam beberapa kasus ini TIDAK akan membulat dengan benar:toFixed () juga TIDAK akan membulat dengan benar dalam beberapa kasus (diuji di Chrome v.55.0.2883.87)!
Contoh:
Saya kira, ini karena 1,555 sebenarnya seperti float 1,55499994 di belakang layar.
Solusi 1 adalah menggunakan skrip dengan algoritma pembulatan yang diperlukan, misalnya:
https://plnkr.co/edit/uau8BlS1cqbvWPCHJeOy?p=preview
CATATAN: Ini bukan solusi universal untuk semua orang. Ada beberapa algoritma pembulatan yang berbeda, implementasi Anda dapat berbeda, tergantung pada kebutuhan Anda. https://en.wikipedia.org/wiki/Rounding
Solusi 2 adalah untuk menghindari perhitungan ujung depan dan menarik nilai bulat dari server backend.
sumber
parseFloat(number.toFixed(decimalPlaces));
@PerLundbergparseFloat("55.555").toFixed(2)
kembali"55.55"
di konsol pengembang Chrome.Kamu bisa menggunakan
Saya menemukan ini di MDN . Cara mereka menghindari masalah dengan 1,005 yang disebutkan .
sumber
+(val)
adalah paksaan menggunakanNumber(val)
. Menggabungkan "e-2" ke nomor menghasilkan string yang perlu dikonversi kembali ke angka.roundToTwo(1.0049999999999999)
keluar sebagai 1,01 (pasti, sejak itu1.0049999999999999 == 1.005
). Sepertinya saya bahwa float yang Anda dapatkan jika Anda mengetiknum = 1.005
'jelas' 'harus' dibulatkan ke 1,00, karena nilai pasti num kurang dari 1,005. Tentu saja, bagi saya nampaknya string '1,005' 'jelas' 'harus' dibulatkan ke 1,01. Fakta bahwa orang yang berbeda tampaknya memiliki intuisi yang berbeda tentang apa perilaku sebenarnya yang benar di sini adalah bagian dari mengapa itu rumit.1.0049999999999999
dan1.005
, jadi menurut definisi, mereka adalah nomor yang sama. Ini disebut potongan dedekind.1.00499 < 1.005
itutrue
,1.0049999999999999 < 1.005
dievaluasi menjadifalse
.Jawaban MarkG adalah yang benar. Berikut ini adalah ekstensi umum untuk sejumlah tempat desimal.
Pemakaian:
Tes unit:
sumber
function round(number, decimals) { return +(Math.round(number + "e+" + decimals) + "e-" + decimals); }
(-1.005).round(2) === -1
Kamu harus menggunakan:
Sepertinya tidak ada yang menyadari
Number.EPSILON
.Juga perlu dicatat bahwa ini bukan keanehan JavaScript seperti yang dikatakan beberapa orang.
Itu hanyalah cara angka floating point bekerja di komputer. Seperti 99% bahasa pemrograman, JavaScript tidak memiliki angka floating point buatan sendiri ; itu bergantung pada CPU / FPU untuk itu. Komputer menggunakan biner, dan dalam biner, tidak ada angka seperti itu
0.1
, tetapi perkiraan biner belaka untuk itu. Mengapa? Untuk alasan yang sama dari 1/3 tidak dapat ditulis dalam desimal: nilainya 0,33333333 ... dengan infinity bertiga.Ini dia
Number.EPSILON
. Angka itu adalah perbedaan antara 1 dan angka berikutnya yang ada dalam angka floating point presisi ganda. Itu dia: Tidak ada angka antara1
dan 1 +Number.EPSILON
.EDIT:
Seperti yang ditanyakan dalam komentar, mari kita perjelas satu hal: menambahkan
Number.EPSILON
hanya relevan ketika nilai untuk dibulatkan adalah hasil dari operasi aritmatika, karena dapat menelan beberapa delta kesalahan floating point.Ini tidak berguna ketika nilainya berasal dari sumber langsung (misalnya: literal, input atau sensor pengguna).
EDIT (2019):
Seperti @maganap dan beberapa orang tunjukkan, sebaiknya tambahkan
Number.EPSILON
sebelum dikalikan:EDIT (desember 2019):
Akhir-akhir ini, saya menggunakan fungsi yang mirip dengan ini untuk membandingkan angka epsilon-aware:
Kasus penggunaan saya adalah pernyataan + validasi data lib yang saya kembangkan selama bertahun-tahun.
Bahkan, dalam kode saya menggunakan
ESPILON_RATE = 1 + 4 * Number.EPSILON
danEPSILON_ZERO = 4 * Number.MIN_VALUE
(empat kali epsilon), karena saya ingin pemeriksa kesetaraan cukup longgar untuk mengumpulkan kesalahan floating point.Sejauh ini, itu terlihat sempurna bagi saya. Saya harap ini akan membantu.
sumber
Math.round( (num + Number.EPSILON) * 100) / 100
.. Saya setuju juga ini adalah metode yang tepat untuk pembulatan yang benar (meskipun tidak persis apa yang diminta dalam pertanyaan ini)0.004999999999999999
akibat dari kesalahan floating point dan hasil yang benar secara matematis mungkin 0,005. Jika itu pembacaan dari sensor? Tidak terlalu banyak.Math.round(1.5)
= 2, tetapiMath.round(-1.5)
= -1. Jadi ini sangat konsisten. Di sini -1 lebih besar dari -2, sama seperti -1000 lebih besar dari -1000.01. Jangan bingung dengan angka absolut yang lebih besar .Orang bisa menggunakan
.toFixed(NumberOfDecimalPlaces)
.sumber
+(1.005).toFixed(2)
yang kembali1
bukan1.01
.Number(9).toFixed(2).replace(/0+$/, '')
=> "9."Pertanyaan ini rumit.
Misalkan kita memiliki fungsi,,
roundTo2DP(num)
yang menggunakan float sebagai argumen dan mengembalikan nilai yang dibulatkan menjadi 2 tempat desimal. Apa yang harus dievaluasi dari masing-masing ekspresi ini?roundTo2DP(0.014999999999999999)
roundTo2DP(0.0150000000000000001)
roundTo2DP(0.015)
Jawaban 'jelas' adalah bahwa contoh pertama harus dibulatkan menjadi 0,01 (karena lebih dekat ke 0,01 daripada 0,02) sedangkan dua lainnya harus bulat menjadi 0,02 (karena 0,015000000000000000001 lebih dekat ke 0,02 daripada 0,01, dan karena 0,015 persis setengah jalan antara mereka dan ada konvensi matematika bahwa angka-angka seperti itu dapat ditangkap).
Tangkapannya, yang mungkin sudah Anda duga, adalah yang
roundTo2DP
tidak mungkin diterapkan untuk memberikan jawaban yang jelas, karena ketiga angka yang diteruskan itu adalah angka yang sama . Angka-angka floating point biner IEEE 754 (jenis yang digunakan oleh JavaScript) tidak bisa persis mewakili sebagian besar angka-angka non-integer, sehingga ketiga literal numerik di atas dibulatkan ke angka floating point terdekat yang valid. Jumlah ini, seperti yang terjadi, tepat0.01499999999999999944488848768742172978818416595458984375
yang lebih dekat ke 0,01 daripada ke 0,02.
Anda dapat melihat bahwa ketiga angka itu sama di konsol browser Anda, Node shell, atau penerjemah JavaScript lainnya. Bandingkan saja mereka:
Jadi ketika saya menulis
m = 0.0150000000000000001
, nilai pastim
yang saya dapatkan lebih dekat0.01
daripada itu0.02
. Namun, jika saya mengonversim
ke String ...... Saya mendapatkan 0,015, yang seharusnya membulatkan ke 0,02, dan yang jelas bukan angka 56-desimal yang sebelumnya saya katakan bahwa semua angka-angka ini persis sama dengan. Jadi sihir gelap apa ini?
Jawabannya dapat ditemukan dalam spesifikasi skrip ECMAS , di bagian 7.1.12.1: ToString diterapkan pada tipe Number . Di sini aturan untuk mengubah beberapa Nomor m menjadi String ditetapkan. Bagian kuncinya adalah titik 5, di mana bilangan bulat s dihasilkan yang digitnya akan digunakan dalam representasi String m :
Bagian kuncinya di sini adalah persyaratan bahwa " k adalah sekecil mungkin". Apa yang dimaksud dengan jumlah persyaratan adalah persyaratan yang, diberikan Nomor
m
, nilaiString(m)
harus memiliki jumlah digit yang paling mungkin sambil tetap memenuhi persyaratan ituNumber(String(m)) === m
. Karena kita sudah tahu itu0.015 === 0.0150000000000000001
, sekarang jelas mengapaString(0.0150000000000000001) === '0.015'
harus benar.Tentu saja, tidak satu pun dari diskusi ini yang langsung menjawab apa yang
roundTo2DP(m)
harus dikembalikan. Jikam
nilai tepatnya adalah 0,01499999999999999949488848768742172978818416595458984375, tetapi representasi String-nya adalah '0,015', lalu apa jawaban yang benar - secara matematis, praktis, filosofis, atau apa pun - ketika kita membulatkannya ke dua tempat desimal?Tidak ada jawaban yang benar untuk ini. Itu tergantung pada kasus penggunaan Anda. Anda mungkin ingin menghormati representasi String dan membulatkan ke atas ketika:
Di sisi lain, Anda mungkin ingin menghormati nilai titik apung biner dan membulatkan ke bawah ketika nilai Anda berasal dari skala inheren terus menerus - misalnya, jika itu pembacaan dari sensor.
Kedua pendekatan ini memerlukan kode yang berbeda. Untuk menghormati representasi String dari Angka, kita dapat (dengan sedikit kode yang cukup halus) menerapkan pembulatan kita sendiri yang bertindak langsung pada representasi String, digit demi digit, menggunakan algoritma yang sama yang akan Anda gunakan di sekolah ketika Anda diajarkan bagaimana membulatkan angka. Di bawah ini adalah contoh yang menghormati persyaratan OP untuk mewakili angka ke 2 tempat desimal "hanya jika perlu" dengan menelusur membuntuti nol setelah titik desimal; Anda mungkin, tentu saja, perlu menyesuaikannya dengan kebutuhan Anda yang sebenarnya.
Contoh penggunaan:
Fungsi di atas mungkin adalah apa yang ingin Anda gunakan untuk menghindari pengguna yang pernah menyaksikan angka yang mereka masukkan dibulatkan secara salah.
(Sebagai alternatif, Anda juga bisa mencoba perpustakaan round10 yang menyediakan fungsi berperilaku serupa dengan implementasi yang sangat berbeda.)
Tetapi bagaimana jika Anda memiliki Bilangan jenis kedua - nilai yang diambil dari skala berkelanjutan, di mana tidak ada alasan untuk berpikir bahwa perkiraan representasi desimal dengan lebih sedikit tempat desimal lebih akurat daripada yang memiliki lebih banyak? Dalam hal ini, kami tidak ingin menghormati representasi String, karena representasi itu (seperti yang dijelaskan dalam spesifikasi) sudah semacam sorting; kami tidak ingin membuat kesalahan dengan mengatakan "0,014999999 ... 375 putaran hingga 0,015, yang bulat hingga 0,02, jadi 0,014999999 ... 375 putaran hingga 0,02".
Di sini kita cukup menggunakan
toFixed
metode bawaan. Perhatikan bahwa dengan memanggilNumber()
String yang dikembalikan olehtoFixed
, kita mendapatkan Nomor yang representasi String-nya tidak memiliki angka nol (berkat cara JavaScript menghitung representasi String dari Angka, yang dibahas sebelumnya dalam jawaban ini).sumber
roundStringNumberWithoutTrailingZeroes(362.42499999999995, 2)
. Hasil yang diharapkan (seperti dalam PHPecho round(362.42499999999995, 2)
):362.43
. Hasil aktual:362.42
round
memberi 362,43. Tampaknya secara intuitif salah, karena 362.4249999999999995 kurang dari 362.425 (dalam matematika dan kode -362.42499999999995 < 362.425
benar dalam JS dan PHP). Jawaban PHP juga tidak meminimalkan jarak antara angka floating point asli dan bulat, sejak362.43 - 362.42499999999995 > 362.42499999999995 - 362.42
. Menurut php.net/manual/en/function.round.php , PHPround
mengikuti standar C99; Saya harus menjelajah ke tanah C untuk memahami apa yang terjadi.Pertimbangkan
.toFixed()
dan.toPrecision()
:http://www.javascriptkit.com/javatutors/formatnumber.shtml
sumber
Metode pembulatan yang tepat. Sumber: Mozilla
Contoh:
sumber
Math.round10(3544.5249, -2)
mengembalikan 3544.52 sebagai gantinya 3544.533544.5249
ke 2 tempat desimal adalah3544.52
(kesalahan = 0,0049). Jika ya3544.53
, kesalahannya adalah 0,0051. Anda melakukan pembulatan berturut-turut yaitu Math.round10 (Math.round10 (3544.5249, -3), -2) yang memberikan kesalahan pembulatan yang lebih besar dan karenanya tidak diinginkan.number += 0.00011
Math.round10( Math.round10(3544.5249, -3) , -2)
Tidak ada jawaban yang ditemukan di sini benar . @stinkycheeseman meminta untuk mengumpulkan , Anda semua membulatkan angka.
Untuk mengumpulkan, gunakan ini:
sumber
Math.ceil(1.1 * 100)/100;
-it kembali1.11
, karena 1.1 * 100110.00000000000001
sesuai dengan browser modern baru Firefox, Chrome, Safari dan Opera ... IE, dengan cara lama, masih berpikir1.1*100=1100
.Math.ceil(num.toFixed(4) * 100) / 100
Math.ceil((1.1).toFixed(4) * 100) / 100
juga akan kembali1.11
di Firefox, masalah / bug browser modern adalah dengan multiplikasi dan orang-orang harus mengetahuinya (saya bekerja pada permainan lotere waktu itu misalnya).Berikut ini cara mudah untuk melakukannya:
Anda mungkin ingin melanjutkan dan membuat fungsi terpisah untuk melakukannya untuk Anda:
Maka Anda cukup memberikan nilai.
Anda bisa meningkatkannya menjadi angka desimal acak dengan menambahkan parameter kedua.
sumber
sumber
+(0.015).toFixed(2) == 0.01
,.Bagi saya Math.round () tidak memberikan jawaban yang benar. Saya menemukan toFixed (2) bekerja lebih baik. Di bawah ini adalah contoh keduanya:
sumber
1.005
.(1.005).toFixed(2)
masih menghasilkan1.00
.Gunakan fungsi ini
Number(x).toFixed(2);
sumber
Number
lagi, jika Anda tidak ingin itu dikembalikan sebagai string:Number(Number(x).toFixed(2));
Number
panggilan tidak perlu,x.toFixed(2)
bekerja.(1).toFixed(2)
kembali1.00
, tetapi penanya diperlukan1
dalam kasus ini.1.005.toFixed(2)
menghasilkan"1"
kapan seharusnya"1.01"
.2017
Cukup gunakan kode asli
.toFixed()
Jika Anda perlu ketat dan menambahkan angka hanya jika diperlukan dapat digunakan
replace
sumber
toFixed
disarankan oleh beberapa jawaban bertahun-tahun sebelum jawaban Anda, tetapi gagal memenuhi persyaratan "hanya jika perlu" dalam pertanyaan;(1).toFixed(2)
memberi"1.00"
tempat yang diinginkan si penanya"1"
.Coba solusi ringan ini :
sumber
return Number(x.toFixed(digits))
?.toFixed()
hanya memungkinkan untuk angka.round(1.005, 2)
dan melihat hasil dari1
bukan1.01
.round(0.995, 2) => 0.99
;round(1.006, 2) => 1.01
;round(1.005, 2) => 1
Ada beberapa cara untuk melakukan itu. Bagi orang-orang seperti saya, varian Lodash
Pemakaian:
Jika proyek Anda menggunakan jQuery atau lodash, Anda juga dapat menemukan
round
metode yang tepat di perpustakaan.Perbarui 1
Saya menghapus varian
n.toFixed(2)
, karena itu tidak benar. Terima kasih @ avalanche1sumber
Number.toFixed()
akan mengembalikan string tetapi dengan simbol plus sebelum itu, JS interpreter akan mengonversi string menjadi angka. Ini adalah gula sintaksis.alert((+1234).toFixed(2))
tampilkan "1234.00".alert(+1234.toFixed(2))
melemparSyntaxError: identifier starts immediately after numeric literal
. Saya tetap dengan opsi 1.362.42499999999995
. Hasil yang diharapkan (seperti dalam PHPecho round(362.42499999999995, 2)
):362.43
. Hasil aktual:362.42
Jika Anda menggunakan pustaka lodash, Anda bisa menggunakan metode bundar lodash seperti berikut.
Misalnya:
sumber
Karena ES6 ada cara yang 'tepat' (tanpa mengesampingkan statika dan membuat solusi) untuk melakukan ini dengan menggunakan toPrecision
maka Anda bisa adil
parseFloat
dan nol akan 'pergi'.Itu tidak memecahkan 'masalah pembulatan 1,005' karena - karena itu intrinsik dengan bagaimana pecahan float sedang diproses .
Jika Anda terbuka untuk perpustakaan, Anda dapat menggunakan bignumber.js
sumber
(1.005).toPrecision(3)
masih kembali,1.00
bukan1.01
sebenarnya.toPrecision
mengembalikan string yang mengubah tipe output yang diinginkan..toPrecision
metode, ini adalah kekhususan angka floating-point (yang angka dalam JS) - coba1.005 - 0.005
, itu akan kembali0.9999999999999999
.(1).toPrecision(3)
mengembalikan '1,00', tetapi penanya ingin memiliki1
dalam kasus ini.toPrecision
Apakah format, bukan yang terakhir, dan bukan jawaban untuk pertanyaan OP, meskipun mungkin tampak relevan pada awalnya itu mendapat banyak kesalahan. Lihat en.wikipedia.org/wiki/Significant_figures . MisalnyaNumber(123.4).toPrecision(2)
pengembalian"1.2e+2"
danNumber(12.345).toPrecision(2)
pengembalian"12"
. Saya juga setuju dengan poin @ adamduren bahwa ia mengembalikan sebuah string yang tidak diinginkan (bukan masalah besar tapi tidak diinginkan).MarkG dan Lavamantis menawarkan solusi yang jauh lebih baik daripada yang telah diterima. Sayang sekali mereka tidak mendapatkan lebih banyak suara!
Berikut adalah fungsi yang saya gunakan untuk memecahkan masalah desimal floating point juga berdasarkan MDN . Itu bahkan lebih umum (tapi kurang ringkas) daripada solusi Lavamantis:
Gunakan dengan:
Dibandingkan dengan solusi Lavamantis, kita dapat melakukan ...
sumber
Ini dapat membantu Anda:
untuk informasi lebih lanjut, Anda dapat melihat tautan ini
Math.round (num) vs num.toFixed (0) dan inkonsistensi peramban
sumber
Pendekatan termudah adalah menggunakan toFixed dan kemudian menghapus angka nol menggunakan fungsi Number:
sumber
15.00
? Angka dalam JS tidak menyimpan tempat desimal dan tampilan apa pun secara otomatis memotong kelebihan tempat desimal (angka nol di bagian akhir).toFixed(2)
di sini 2 adalah jumlah digit hingga kami ingin membulatkan angka ini.sumber
Ini mungkin bekerja untuk Anda,
untuk mengetahui perbedaan antara toFixed dan round. Anda dapat melihat Math.round (num) vs num.toFixed (0) dan inkonsistensi peramban .
sumber
Cara termudah:
+num.toFixed(2)
Ini mengubahnya menjadi string, dan kemudian kembali ke integer / float.
sumber
toFixed()
ke 3. Jadi+num.toFixed(3)
. Itu bekerja dengan cara yang seharusnya, 1,005 dibulatkan menjadi 1,00, yang sama dengan 1Berikut adalah metode prototipe:
sumber
Gunakan sesuatu seperti ini "parseFloat (parseFloat (value) .toFixed (2))"
sumber
Salah satu cara untuk mencapai pembulatan seperti itu hanya jika perlu adalah dengan menggunakan Number.prototype.toLocaleString () :
Ini akan memberikan persis output yang Anda harapkan, tetapi sebagai string. Anda masih bisa mengonversi angka itu kembali ke angka jika itu bukan tipe data yang Anda harapkan.
sumber
toLocaleString
.Setelah menjalankan berbagai iterasi dari semua cara yang mungkin untuk mencapai presisi pembulatan desimal akurat, jelas bahwa solusi yang paling akurat dan efisien adalah dengan menggunakan Number.EPSILON. Ini memberikan solusi matematika yang benar untuk masalah presisi matematika floating point. Hal ini dapat dengan mudah diisi polif seperti yang ditunjukkan di sini: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/EPSILON untuk mendukung semua pengguna IE yang tersisa terakhir (sekali lagi mungkin kita harus berhenti melakukan itu).
Diadaptasi dari solusi yang disediakan di sini: https://stackoverflow.com/a/48850944/6910392
Solusi penurunan sederhana yang memberikan pembulatan desimal, lantai, dan langit-langit yang akurat, dengan variabel presisi opsional tanpa menambahkan seluruh perpustakaan.
UPDATE: Seperti yang dicatat Sergey dalam komentar, ada batasan untuk metode ini (atau apa pun) yang pantas untuk ditunjukkan. Dalam hal angka-angka seperti 0,014999999999999999, Anda masih akan mengalami ketidakakuratan yang merupakan hasil dari memukul tepi mutlak keterbatasan akurasi untuk penyimpanan nilai floating point. Tidak ada matematika atau solusi lain yang dapat diterapkan untuk menjelaskan itu, karena nilai itu sendiri segera dievaluasi sebagai 0,015. Anda dapat mengonfirmasi ini dengan hanya memanggil nilai itu sendiri di konsol. Karena keterbatasan ini, bahkan tidak mungkin untuk menggunakan manipulasi string untuk mengurangi nilai ini, karena representasi stringnya hanyalah "0,015". Solusi apa pun untuk memperhitungkan ini perlu diterapkan secara logis pada sumber data sebelum pernah menerima nilai ke dalam skrip,
sumber
Ini adalah solusi paling sederhana dan lebih elegan (dan saya yang terbaik di dunia;):
sumber
E
notasi.roundToX(362.42499999999995, 2)
. Hasil yang diharapkan (seperti dalam PHPecho round(362.42499999999995, 2)
):362.43
. Hasil aktual:362.42