Bagaimana saya bisa membulatkan angka dalam JavaScript? .toFixed () mengembalikan string?

176

Apakah saya melewatkan sesuatu di sini?

var someNumber = 123.456;
someNumber = someNumber.toFixed(2);
alert(typeof(someNumber));
//alerts string

Mengapa tidak.toFixed()kembali string?

Saya ingin membulatkan angka menjadi 2 angka desimal.

Derek Adair
sumber
7
Karena dirancang untuk mengembalikan string?
kennytm
2
Bagi saya sepertinya aneh. .toFixed () hanya beroperasi pada angka ... bukan?
Derek Adair
10
Saya mengerti Math.round () berfungsi seperti yang diharapkan. Saya hanya ingin tahu mengapa fungsi yang beroperasi pada angka mengembalikan sebuah string ...
Derek Adair
3
Orang yang tinggal di 2017 harus menggunakan perpustakaan seperti lodash.com/docs/4.17.4#ceil
Yves M.
1
Begitu juga _. menghitung? belum ditingkatkan ke bro-nya.
Jenna Leaf

Jawaban:

124

Ini mengembalikan string karena 0,1, dan kekuatannya (yang digunakan untuk menampilkan pecahan desimal), tidak dapat diwakili (setidaknya tidak dengan akurasi penuh) dalam sistem titik-mengambang biner.

Sebagai contoh, 0,1 benar-benar 0,1000000000000000055511151231257827021181583404541015625, dan 0,01 benar-benar 0,01000000000000000020816681711721685132943093776702880859375. (Terima kasih BigDecimaltelah membuktikan pendapat saya. :-P)

Oleh karena itu (tidak ada floating point desimal atau tipe angka rasional), mengeluarkannya sebagai string adalah satu-satunya cara untuk memangkasnya dengan presisi yang dibutuhkan untuk tampilan.

Chris Jester-Young
sumber
28
setidaknya javascript bisa menyelamatkan saya dari beberapa pekerjaan jari dan mengubahnya kembali ke nomor ... sheesh ...
Derek Adair
10
@ Serek: Ya, tapi begitu Anda mengubahnya kembali menjadi angka, Anda mengalami masalah ketidakakuratan yang sama lagi. :-P JS tidak memiliki floating point desimal atau angka rasional.
Chris Jester-Young
1
@DerekAdair Saya baru saja menulis posting yang menjelaskan ini lebih jauh, yang mungkin Anda tertarik. Selamat menikmati! stackoverflow.com/a/27030789/13
Chris Jester-Young
7
Sebenarnya ini menyebabkan saya melakukan penelitian yang cukup berat pada subjek ini! Terima kasih atas seluruh bantuan Anda!
Derek Adair
2
Jawaban Anda sedikit menyesatkan: toFixedadalah fungsi pemformatan, yang memiliki tujuan tunggal mengonversi angka menjadi string, memformatnya menggunakan jumlah desimal yang ditentukan. Alasan mengembalikan string adalah karena seharusnya mengembalikan string, dan jika itu dinamai toStringFixedsebaliknya, OP tidak akan terkejut dengan hasilnya. Satu-satunya masalah di sini adalah bahwa OP diharapkan berfungsi Math.round, tanpa berkonsultasi dengan referensi JS.
Groo
177

Number.prototype.toFixedadalah fungsi yang dirancang untuk memformat angka sebelum mencetaknya. Itu dari keluarga toString, toExponentialdantoPrecision .

Untuk membulatkan angka, Anda akan melakukan ini:

someNumber = 42.008;
someNumber = Math.round( someNumber * 1e2 ) / 1e2;
someNumber === 42.01;

// if you need 3 digits, replace 1e2 with 1e3 etc.
// or just copypaste this function to your code:

function toFixedNumber(num, digits, base){
  var pow = Math.pow(base||10, digits);
  return Math.round(num*pow) / pow;
}

.

Atau jika Anda menginginkan fungsi " mirip-asli ", Anda dapat memperluas prototipe:

Number.prototype.toFixedNumber = function(digits, base){
  var pow = Math.pow(base||10, digits);
  return Math.round(this*pow) / pow;
}
someNumber = 42.008;
someNumber = someNumber.toFixedNumber(2);
someNumber === 42.01;


//or even hexadecimal

someNumber = 0xAF309/256  //which is af3.09
someNumber = someNumber.toFixedNumber(1, 16);
someNumber.toString(16) === "af3.1";

Namun , ingatlah bahwa mencemari prototipe dianggap buruk ketika Anda menulis modul, karena modul tidak boleh memiliki efek samping. Jadi, untuk modul, gunakan fungsi pertama .

m93a
sumber
12
Saya pikir ini adalah jawaban terbaik. Ini menghindari konversi tipe. Saus yang luar biasa!
Phil
1
Jawaban bagus! Namun ... Saya telah melakukan JavaScript seperti sekitar 20 tahun, tetapi saya tidak tahu mengapa Anda menggunakan konstruksi + (...) di sekitar nilai pengembalian? Terima kasih @sam untuk mengoleskannya :) Karena saya tidak pernah terlalu tua untuk belajar, tolong jelaskan :-)
HammerNL
1
@HammerNL Meskipun keyakinan Sam, secara akut tidak melakukan apa-apa :) Ini hanya latihan - itu membuat IDE mengenali fungsi ini sebagai type Number. Masalahnya adalah bahwa +(anyValue)selalu mengembalikan angka - misalnya. +("45")kembali 45, +(new Number(42))kembali 42. Ini seperti mengetik fungsi dengan kuat. Jika Anda terbiasa, Anda dapat menghindari banyak bug :)
m93a
Mengapa ini tidak dimasukkan ke dalam javascript inti: s
webmaster
2
Penyesuaian someNumber = Math.round( 42.008 * 1e2 ) / 1e2;tidak 42.01, itu ~42.0099999999999980. Alasan: Nomor 42.01itu tidak ada dan dibulatkan ke nomor terdekat yang ada. btw, nomor bukti dengan toPrecision(18)mencetaknya dengan semua digit yang relevan.
Wiimm
118

Saya telah memecahkan masalah ini dengan mengubah ini:

someNumber = someNumber.toFixed(2)

...untuk ini:

someNumber = +someNumber.toFixed(2);

Namun ini akan mengubah angka menjadi string dan menguraikannya lagi, yang akan berdampak signifikan pada kinerja. Jika Anda peduli dengan kinerja atau keamanan jenis, periksa jawaban lainnya juga.

Eve juan
sumber
39
Tidak, tidak, tidak, tidak, tidak! Jangan lakukan itu! Konversi angka ke string hanya untuk membulatkannya adalah praktik yang sangat buruk ! Sebaliknya lakukan someNumber = Math.round(someNumber * 1e2) / 1e2! Lihat jawaban saya untuk cara yang lebih umum.
m93a
@ m93a - mengapa itu praktik yang buruk?
jczaplew
3
@jczaplew Karena jika Anda melakukannya dengan cara ini, angka biner 32bit dikonversi menjadi string, menggunakan 16bits untuk setiap angka desimal ! (Omong-omong menyimpan nomor dalam UTF-16 bukan hal yang paling nyaman Anda akan pergi.) Dan daripada string dikonversi kembali ke angka titik-mengambang 32bit. Digit demi digit. (Jika saya mengabaikan semua tes yang harus dilakukan sebelumnya untuk memilih algoritma parsing yang tepat.) Dan semua itu sia-sia mengingat Anda dapat melakukannya dengan menggunakan 3 operasi cepat di float.
m93a
2
@ m93a jadi untuk alasan kinerja? apakah ini sebenarnya memiliki dampak yang nyata dalam kinerja?
Sebastianb
2
@ jczaplew, Karena Strings (1) praktis lambat dan (2) secara teoritis salah.
Pacerier
28

Kenapa tidak digunakan parseFloat?

var someNumber = 123.456;
someNumber = parseFloat(someNumber.toFixed(2));
alert(typeof(someNumber));
//alerts number
sirlunchalot
sumber
15

Saya menyelesaikannya dengan mengubahnya kembali ke angka menggunakan Number()fungsi JavaScript

var x = 2.2873424;
x = Number(x.toFixed(2));
Nizar
sumber
12

Tentu saja mengembalikan string. Jika Anda ingin membulatkan variabel numerik, Anda akan menggunakan Math.round (). Maksud dari toFixed adalah untuk memformat angka dengan jumlah tetap tempat desimal untuk ditampilkan kepada pengguna .

Joel Coehoorn
sumber
4

Anda cukup menggunakan tanda '+' untuk mengonversi hasilnya menjadi angka.

var x = 22.032423;
x = +x.toFixed(2); // x = 22.03
meisam
sumber
3

Apa yang Anda harapkan untuk dikembalikan ketika seharusnya memformat angka? Jika Anda memiliki nomor, Anda tidak bisa melakukan apa-apa karena mis 2 == 2.0 == 2.00. Dll. Jadi harus berupa string.

Tomas Vana
sumber
3

Untuk memberikan contoh mengapa harus berupa string:

Jika Anda memformat 1.toFixed (2) Anda akan mendapatkan '1,00'.

Ini tidak sama dengan 1, karena 1 tidak memiliki 2 desimal.


Saya tahu JavaScript bukan bahasa kinerja , tetapi kemungkinan Anda akan mendapatkan kinerja yang lebih baik untuk pembulatan jika Anda menggunakan sesuatu seperti: roundedValue = Math.round (nilai * 100) * 0,01

Pyro
sumber
2

Karena penggunaan utamanya adalah menampilkan angka? Jika Anda ingin membulatkan angka, gunakan Math.round()dengan faktor yang sesuai.

Christoph
sumber
tapi ini menampilkan ANGKA jadi bukankah seharusnya mengembalikan "angka"?
Derek Adair
3
@ Serek: Hanya dengan cara yang '42'merupakan angka ... yang bukan. Hanya karena sebuah string berisi digit saja tidak membuatnya menjadi angka. Ini bukan PHP. :-P
Chris Jester-Young
lol. Ini bukan string yang berisi angka ... Ini nomor yang diteruskan ke metode. Metode ini mengambil angka dan mengembalikan string.
Derek Adair
@DerekAdair benar tetapi browser tidak dapat menampilkan Angka, ini menampilkan string, sehingga konversi.
Nick M
1

Inilah versi yang sedikit lebih fungsional dari jawaban yang m93adiberikan.

const toFixedNumber = (toFixTo = 2, base = 10) => num => {
  const pow = Math.pow(base, toFixTo)
  return +(Math.round(num * pow) / pow)
}

const oneNumber = 10.12323223

const result1 = toFixedNumber(2)(oneNumber) // 10.12
const result2 = toFixedNumber(3)(oneNumber) // 10.123

// or using pipeline-operator
const result3 = oneNumber |> toFixedNumber(2) // 10.12
Sartaj
sumber
itu fungsi praktis, untuk tipe yang tidak ditentukan itu tidak berfungsi, saya telah menambahkan kode untuk kasus ini; if (num! == undefined) {return + (Math.round (num * pow) / pow)} else {return 0; }
Dino Liu