Dalam JavaScript modern, ada dua jenis angka:
Angka reguler dalam JavaScript disimpan dalam format 64-bit IEEE-754, juga dikenal sebagai “angka floating point presisi ganda”. Ini adalah angka-angka yang paling sering kita gunakan, dan kita akan membicarakannya di bab ini.
Angka BigInt mewakili bilangan bulat dengan panjang sembarang. Terkadang dibutuhkan karena bilangan bulat biasa tidak bisa melebihi (2 53 -1)
atau kurang dari -(2 53 -1)
, seperti yang telah kami sebutkan sebelumnya di bab Tipe data. Karena bigint digunakan di beberapa area khusus, kami mencurahkannya ke bab khusus BigInt.
Jadi di sini kita akan berbicara tentang bilangan reguler. Mari kita perluas pengetahuan kita tentang mereka.
Bayangkan kita perlu menulis 1 miliar. Cara yang jelas adalah:
misalkan miliar = 1000000000;
Kita juga bisa menggunakan garis bawah _
sebagai pemisah:
misalkan miliar = 1_000_000_000;
Di sini garis bawah _
berperan sebagai “gula sintaksis”, membuat angka lebih mudah dibaca. Mesin JavaScript mengabaikan _
antar digit, jadi satu miliar sama persis seperti di atas.
Namun dalam kehidupan nyata, kami mencoba menghindari penulisan angka nol yang panjang. Kami terlalu malas untuk itu. Kita akan mencoba menulis sesuatu seperti "1bn"
untuk satu miliar atau "7.3bn"
untuk 7 miliar 300 juta. Hal yang sama berlaku untuk sebagian besar bilangan besar.
Dalam JavaScript, kita dapat mempersingkat suatu angka dengan menambahkan huruf "e"
ke dalamnya dan menentukan jumlah angka nolnya:
misalkan miliar = 1e9; // 1 miliar, secara harafiah: 1 dan 9 nol peringatan( 7.3e9 ); // 7,3 miliar (sama dengan 7300000000 atau 7_300_000_000)
Dengan kata lain, e
mengalikan angka tersebut dengan 1
dengan jumlah angka nol yang diberikan.
1e3 === 1*1000; // e3 berarti *1000 1,23e6 === 1,23 * 1.000.000; // e6 artinya *1000000
Sekarang mari kita menulis sesuatu yang sangat kecil. Katakanlah, 1 mikrodetik (sepersejuta detik):
misalkan mсs = 0,000001;
Sama seperti sebelumnya, menggunakan "e"
bisa membantu. Jika kita ingin menghindari penulisan angka nol secara eksplisit, kita dapat menulis hal yang sama seperti:
misalkan mcs = 1e-6; // lima angka nol ke kiri dari 1
Jika kita menghitung angka nol di 0.000001
, ada 6 angka. Jadi wajar saja 1e-6
.
Dengan kata lain, bilangan negatif setelah "e"
berarti pembagian dengan 1 dengan jumlah nol tertentu:
// -3 dibagi 1 dengan 3 angka nol 1e-3 === 1/1000; // 0,001 // -6 dibagi 1 dengan 6 angka nol 1,23e-6 === 1,23 / 1.000.000; // 0,00000123 // contoh dengan angka yang lebih besar 1234e-2 === 1234/100; // 12.34, koma desimal berpindah 2 kali
Angka heksadesimal banyak digunakan dalam JavaScript untuk merepresentasikan warna, menyandikan karakter, dan banyak hal lainnya. Jadi wajar saja, ada cara yang lebih singkat untuk menuliskannya: 0x
lalu angkanya.
Misalnya:
peringatan( 0xff ); // 255 peringatan( 0xFF ); // 255 (sama saja, huruf besar/kecil tidak masalah)
Sistem bilangan biner dan oktal jarang digunakan, tetapi juga didukung dengan menggunakan awalan 0b
dan 0o
:
misalkan a = 0b11111111; // bentuk biner dari 255 misalkan b = 0o377; //bentuk oktal dari 255 waspada( a == b ); // benar, angka yang sama 255 di kedua sisi
Hanya ada 3 sistem angka dengan dukungan seperti itu. Untuk sistem bilangan lainnya, kita harus menggunakan fungsi parseInt
(yang akan kita lihat nanti di bab ini).
Metode num.toString(base)
mengembalikan representasi string num
dalam sistem angka dengan base
yang diberikan.
Misalnya:
misalkan bilangan = 255; peringatan( nomor.toString(16) ); // ff peringatan( nomor.toString(2) ); // 11111111
base
dapat bervariasi dari 2
hingga 36
. Secara default, itu 10
.
Kasus penggunaan umum untuk ini adalah:
base=16 digunakan untuk warna hex, pengkodean karakter dll, digit bisa 0..9
atau A..F
.
base=2 sebagian besar untuk men-debug operasi bitwise, angkanya bisa 0
atau 1
.
base=36 adalah maksimum, digit bisa 0..9
atau A..Z
. Seluruh alfabet Latin digunakan untuk mewakili suatu angka. Kasus yang lucu namun berguna untuk 36
adalah ketika kita perlu mengubah pengenal numerik yang panjang menjadi sesuatu yang lebih pendek, misalnya, untuk membuat url pendek. Cukup dapat direpresentasikan dalam sistem angka dengan basis 36
:
peringatan( 123456..toString(36) ); // 2n9c
Dua titik untuk memanggil suatu metode
Harap dicatat bahwa dua titik di 123456..toString(36)
bukan salah ketik. Jika kita ingin memanggil suatu metode secara langsung pada suatu nomor, seperti toString
pada contoh di atas, maka kita perlu menempatkan dua titik ..
setelahnya.
Jika kita menempatkan satu titik: 123456.toString(36)
, maka akan terjadi kesalahan, karena sintaksis JavaScript menyiratkan bagian desimal setelah titik pertama. Dan jika kita menempatkan satu titik lagi, maka JavaScript mengetahui bahwa bagian desimalnya kosong dan sekarang masuk ke metodenya.
Juga bisa menulis (123456).toString(36)
.
Salah satu operasi yang paling sering digunakan saat bekerja dengan angka adalah pembulatan.
Ada beberapa fungsi bawaan untuk pembulatan:
Math.floor
Pembulatan ke bawah: 3.1
menjadi 3
, dan -1.1
menjadi -2
.
Math.ceil
Pembulatan: 3.1
menjadi 4
, dan -1.1
menjadi -1
.
Math.round
Pembulatan ke bilangan bulat terdekat: 3.1
menjadi 3
, 3.6
menjadi 4
. Dalam kasus tengah 3.5
putaran hingga 4
, dan -3.5
putaran hingga -3
.
Math.trunc
(tidak didukung oleh Internet Explorer)
Menghapus apa pun setelah koma desimal tanpa pembulatan: 3.1
menjadi 3
, -1.1
menjadi -1
.
Berikut tabel untuk merangkum perbedaan di antara keduanya:
Math.floor | Math.ceil | Math.round | Math.trunc | |
---|---|---|---|---|
3.1 | 3 | 4 | 3 | 3 |
3.5 | 3 | 4 | 4 | 3 |
3.6 | 3 | 4 | 4 | 3 |
-1.1 | -2 | -1 | -1 | -1 |
-1.5 | -2 | -1 | -1 | -1 |
-1.6 | -2 | -1 | -2 | -1 |
Fungsi-fungsi ini mencakup semua cara yang mungkin untuk menangani bagian desimal suatu angka. Namun bagaimana jika kita ingin membulatkan angka tersebut ke digit ke n-th
setelah desimal?
Misalnya, kita punya 1.2345
dan ingin membulatkannya menjadi 2 digit, sehingga hanya mendapatkan 1.23
.
Ada dua cara untuk melakukannya:
Kalikan dan bagi.
Misalnya, untuk membulatkan suatu bilangan ke angka ke-2 setelah desimal, kita dapat mengalikan bilangan tersebut dengan 100
, memanggil fungsi pembulatan, lalu membaginya kembali.
misalkan bilangan = 1,23456; alert( Math.round(angka * 100) / 100 ); // 1.23456 -> 123.456 -> 123 -> 1.23
Metode toFixed(n) membulatkan angka menjadi n
digit setelah titik dan mengembalikan representasi string dari hasilnya.
misalkan bilangan = 12,34; peringatan( nomor.toFixed(1) ); // "12.3"
Ini membulatkan ke atas atau ke bawah ke nilai terdekat, mirip dengan Math.round
:
misalkan bilangan = 12,36; peringatan( nomor.toFixed(1) ); // "12.4"
Perlu diketahui bahwa hasil toFixed
adalah sebuah string. Jika bagian desimal lebih pendek dari yang disyaratkan, angka nol ditambahkan di akhir:
misalkan bilangan = 12,34; peringatan( nomor.toFixed(5) ); // "12.34000", menambahkan angka nol sehingga menjadi tepat 5 digit
Kita dapat mengonversinya menjadi angka menggunakan panggilan unary plus atau Number()
, misalnya write +num.toFixed(5)
.
Secara internal, suatu bilangan direpresentasikan dalam format 64-bit IEEE-754, jadi terdapat tepat 64 bit untuk menyimpan suatu bilangan: 52 bit digunakan untuk menyimpan digit, 11 bit menyimpan posisi koma desimal, dan 1 bit adalah untuk tandanya.
Jika angkanya sangat besar, angka tersebut mungkin akan meluap ke penyimpanan 64-bit dan menjadi nilai numerik khusus Infinity
:
peringatan( 1e500 ); // Ketakterbatasan
Hal yang mungkin kurang jelas, namun cukup sering terjadi, adalah hilangnya presisi.
Pertimbangkan tes kesetaraan (yang salah!) ini:
peringatan( 0,1 + 0,2 == 0,3 ); // PALSU
Benar, jika kita memeriksa apakah jumlah 0.1
dan 0.2
adalah 0.3
, kita mendapatkan false
.
Aneh! Lalu apa jadinya jika bukan 0.3
?
peringatan( 0,1 + 0,2 ); // 0,30000000000000004
Aduh! Bayangkan Anda membuat situs belanja elektronik dan pengunjung memasukkan barang senilai $0.10
dan $0.20
ke dalam keranjang mereka. Total pesanan akan menjadi $0.30000000000000004
. Itu akan mengejutkan siapa pun.
Namun mengapa hal ini bisa terjadi?
Suatu angka disimpan dalam memori dalam bentuk binernya, urutan bit – satu dan nol. Namun pecahan seperti 0.1
, 0.2
yang terlihat sederhana dalam sistem angka desimal sebenarnya adalah pecahan tak berujung dalam bentuk binernya.
peringatan(0.1.toString(2)); // 0,0001100110011001100110011001100110011001100110011001101 peringatan(0.2.toString(2)); // 0,001100110011001100110011001100110011001100110011001101 peringatan((0,1 + 0,2).toString(2)); // 0,0100110011001100110011001100110011001100110011001101
Apa itu 0.1
? Itu satu dibagi sepuluh 1/10
, sepersepuluh. Dalam sistem angka desimal, angka-angka tersebut mudah direpresentasikan. Bandingkan dengan sepertiga: 1/3
. Ini menjadi pecahan tak berujung 0.33333(3)
.
Jadi, pembagian dengan pangkat 10
dijamin berhasil dengan baik dalam sistem desimal, namun pembagian dengan 3
tidak. Untuk alasan yang sama, dalam sistem bilangan biner, pembagian dengan pangkat 2
dijamin berhasil, tetapi 1/10
menjadi pecahan biner tak berujung.
Tidak ada cara untuk menyimpan tepat 0,1 atau 0,2 menggunakan sistem biner, sama seperti tidak ada cara untuk menyimpan sepertiga sebagai pecahan desimal.
Format numerik IEEE-754 menyelesaikan masalah ini dengan membulatkan ke angka terdekat. Aturan pembulatan ini biasanya tidak memungkinkan kita untuk melihat “kehilangan presisi yang kecil”, namun hal ini memang ada.
Kita dapat melihat ini dalam tindakan:
peringatan( 0.1.toFixed(20) ); // 0,10000000000000000555
Dan saat kita menjumlahkan dua angka, “kerugian presisi” keduanya bertambah.
Itu sebabnya 0.1 + 0.2
tidak persis 0.3
.
Tidak hanya JavaScript
Masalah yang sama juga terjadi di banyak bahasa pemrograman lainnya.
PHP, Java, C, Perl, dan Ruby memberikan hasil yang persis sama, karena didasarkan pada format numerik yang sama.
Bisakah kita mengatasi masalah ini? Tentu saja, metode yang paling dapat diandalkan adalah membulatkan hasilnya dengan bantuan metode toFixed(n):
misalkan jumlah = 0,1 + 0,2; peringatan( jumlah.toFixed(2) ); // "0,30"
Harap dicatat bahwa toFixed
selalu mengembalikan string. Ini memastikan bahwa ia memiliki 2 digit setelah koma desimal. Itu sebenarnya nyaman jika kita memiliki e-shopping dan perlu menunjukkan $0.30
. Untuk kasus lain, kita bisa menggunakan unary plus untuk memaksanya menjadi angka:
misalkan jumlah = 0,1 + 0,2; peringatan( +jumlah.toFixed(2) ); // 0.3
Kita juga dapat mengalikan angka tersebut untuk sementara dengan 100 (atau angka yang lebih besar) untuk mengubahnya menjadi bilangan bulat, menghitungnya, lalu membaginya kembali. Kemudian, saat kita mengerjakan matematika dengan bilangan bulat, kesalahannya sedikit berkurang, namun kita masih mendapatkannya pada pembagian:
peringatan( (0,1*10 + 0,2*10) / 10 ); // 0.3 peringatan( (0,28*100 + 0,14*100) / 100); // 0,4200000000000001
Jadi, pendekatan perkalian/bagi mengurangi kesalahan, namun tidak menghilangkannya sepenuhnya.
Terkadang kita bisa mencoba menghindari pecahan sama sekali. Misalnya jika kita berurusan dengan toko, maka kita dapat menyimpan harga dalam sen, bukan dolar. Namun bagaimana jika kita menerapkan diskon 30%? Dalam praktiknya, menghindari pecahan sepenuhnya jarang bisa dilakukan. Bulatkan saja untuk memotong “ekor” bila diperlukan.
Hal yang lucu
Coba jalankan ini:
// Halo! Saya adalah angka yang bertambah sendiri! waspada( 9999999999999999 ); // menampilkan 10000000000000000
Hal ini juga mengalami masalah yang sama: hilangnya presisi. Ada 64 bit untuk suatu angka, 52 bit di antaranya dapat digunakan untuk menyimpan angka, tetapi itu tidak cukup. Jadi angka paling kecil berarti hilang.
JavaScript tidak memicu kesalahan dalam kejadian seperti itu. Ini melakukan yang terbaik untuk memasukkan angka ke dalam format yang diinginkan, namun sayangnya, format ini tidak cukup besar.
Dua angka nol
Konsekuensi lucu lainnya dari representasi internal angka adalah adanya dua angka nol: 0
dan -0
.
Itu karena suatu tanda diwakili oleh satu bit, sehingga dapat disetel atau tidak untuk bilangan apa pun termasuk nol.
Dalam kebanyakan kasus, perbedaannya tidak terlalu mencolok, karena operator cocok untuk memperlakukan keduanya dengan cara yang sama.
Ingat dua nilai numerik khusus ini?
Infinity
(dan -Infinity
) adalah nilai numerik khusus yang lebih besar (lebih kecil) dari apapun.
NaN
mewakili kesalahan.
Mereka termasuk dalam tipe number
, tetapi bukan angka “normal”, jadi ada fungsi khusus untuk memeriksanya:
isNaN(value)
mengonversi argumennya menjadi angka dan kemudian mengujinya sebagai NaN
:
peringatan( isNaN(NaN) ); // BENAR peringatan( isNaN("str") ); // BENAR
Tapi apakah kita memerlukan fungsi ini? Tidak bisakah kita menggunakan perbandingan === NaN
saja? Sayangnya tidak. Nilai NaN
unik karena tidak sama dengan apa pun, termasuk dirinya sendiri:
peringatan( NaN === NaN ); // PALSU
isFinite(value)
mengonversi argumennya menjadi angka dan mengembalikan true
jika angkanya biasa, bukan NaN/Infinity/-Infinity
:
peringatan( isFinite("15") ); // BENAR peringatan( isFinite("str") ); // salah, karena nilai khusus: NaN peringatan( isFinite(Infinity) ); // salah, karena nilai khusus: Tak terhingga
Terkadang isFinite
digunakan untuk memvalidasi apakah nilai string adalah angka biasa:
biarkan num = +prompt("Masukkan angka", ''); // akan menjadi benar kecuali Anda memasukkan Infinity, -Infinity atau bukan angka waspada( isFinite(angka) );
Harap perhatikan bahwa string kosong atau hanya spasi diperlakukan sebagai 0
di semua fungsi numerik termasuk isFinite
.
Number.isNaN
dan Number.isFinite
Metode Number.isNaN dan Number.isFinite adalah versi fungsi isNaN
dan isFinite
yang lebih “ketat”. Mereka tidak secara otomatis mengubah argumennya menjadi angka, namun memeriksa apakah argumen tersebut termasuk dalam tipe number
.
Number.isNaN(value)
mengembalikan true
jika argumennya termasuk dalam tipe number
dan merupakan NaN
. Dalam kasus lain, ia mengembalikan false
.
alert( Nomor.isNaN(NaN) ); // BENAR alert( Nomor.isNaN("str" / 2) ); // BENAR // Perhatikan perbedaannya: alert( Nomor.isNaN("str") ); // salah, karena "str" termasuk dalam tipe string, bukan tipe angka peringatan( isNaN("str") ); // benar, karena isNaN mengubah string "str" menjadi angka dan mendapatkan NaN sebagai hasil konversi ini
Number.isFinite(value)
mengembalikan true
jika argumen termasuk dalam tipe number
dan bukan NaN/Infinity/-Infinity
. Dalam kasus lain, ia mengembalikan false
.
alert( Number.isFinite(123) ); // BENAR alert( Number.isFinite(Infinity) ); // PALSU alert( Number.isFinite(2 / 0) ); // PALSU // Perhatikan perbedaannya: alert( Number.isFinite("123") ); // salah, karena "123" termasuk dalam tipe string, bukan tipe angka peringatan( isFinite("123") ); // benar, karena isFinite mengubah string "123" menjadi angka 123
Di satu sisi, Number.isNaN
dan Number.isFinite
lebih sederhana dan mudah dibandingkan fungsi isNaN
dan isFinite
. Namun dalam praktiknya, isNaN
dan isFinite
paling banyak digunakan, karena lebih pendek untuk ditulis.
Perbandingan dengan Object.is
Ada metode bawaan khusus Object.is
yang membandingkan nilai seperti ===
, tetapi lebih dapat diandalkan untuk dua kasus tepi:
Ia bekerja dengan NaN
: Object.is(NaN, NaN) === true
, itu hal yang bagus.
Nilai 0
dan -0
berbeda: Object.is(0, -0) === false
, secara teknis itu benar karena secara internal angka tersebut memiliki bit tanda yang mungkin berbeda meskipun semua bit lainnya nol.
Dalam semua kasus lainnya, Object.is(a, b)
sama dengan a === b
.
Kami menyebutkan Object.is
di sini, karena sering digunakan dalam spesifikasi JavaScript. Ketika algoritme internal perlu membandingkan dua nilai agar sama persis, algoritme tersebut menggunakan Object.is
(secara internal disebut SameValue).
Konversi numerik menggunakan tanda tambah +
atau Number()
sangatlah ketat. Jika suatu nilai bukan angka, maka gagal:
peringatan( +"100 piksel" ); // Tidak
Satu-satunya pengecualian adalah spasi di awal atau di akhir string, karena spasi tersebut diabaikan.
Namun dalam kehidupan nyata, kita sering kali memiliki nilai dalam satuan, seperti "100px"
atau "12pt"
di CSS. Juga di banyak negara, simbol mata uang berada setelah jumlah, jadi kami memiliki "19€"
dan ingin mengambil nilai numerik dari simbol tersebut.
Itulah gunanya parseInt
dan parseFloat
.
Mereka “membaca” nomor dari sebuah string sampai mereka tidak bisa. Jika terjadi kesalahan, nomor yang dikumpulkan akan dikembalikan. Fungsi parseInt
mengembalikan bilangan bulat, sedangkan parseFloat
akan mengembalikan bilangan floating-point:
peringatan( parseInt('100px') ); // 100 peringatan( parseFloat('12.5em') ); // 12.5 peringatan( parseInt('12.3') ); // 12, hanya bagian bilangan bulat yang dikembalikan peringatan( parseFloat('12.3.4') ); // 12.3, poin kedua menghentikan pembacaan
Ada situasi ketika parseInt/parseFloat
akan mengembalikan NaN
. Ini terjadi ketika tidak ada angka yang dapat dibaca:
peringatan( parseInt('a123') ); // NaN, simbol pertama menghentikan proses
Argumen kedua parseInt(str, radix)
Fungsi parseInt()
memiliki parameter kedua opsional. Ini menentukan basis sistem angka, sehingga parseInt
juga dapat mengurai rangkaian bilangan hex, bilangan biner, dan seterusnya:
peringatan( parseInt('0xff', 16) ); // 255 peringatan( parseInt('ff', 16) ); // 255, tanpa 0x juga berfungsi peringatan( parseInt('2n9c', 36) ); // 123456
JavaScript memiliki objek Math bawaan yang berisi perpustakaan kecil fungsi dan konstanta matematika.
Beberapa contoh:
Math.random()
Mengembalikan nomor acak dari 0 hingga 1 (tidak termasuk 1).
waspada( Matematika.acak() ); // 0,1234567894322 waspada( Matematika.acak() ); // 0,5435252343232 waspada( Matematika.acak() ); // ... (nomor acak apa saja)
Math.max(a, b, c...)
dan Math.min(a, b, c...)
Mengembalikan argumen terbesar dan terkecil dari sejumlah argumen yang berubah-ubah.
waspada( Matematika.max(3, 5, -10, 0, 1) ); // 5 waspada( Matematika.min(1, 2) ); // 1
Math.pow(n, power)
Mengembalikan n
dipangkatkan ke pangkat tertentu.
waspada( Matematika.pow(2, 10) ); // 2 pangkat 10 = 1024
Ada lebih banyak fungsi dan konstanta dalam objek Math
, termasuk trigonometri, yang dapat Anda temukan di dokumen untuk objek Math.
Untuk menulis angka dengan banyak angka nol:
Tambahkan "e"
dengan angka nol dihitung ke nomor tersebut. Seperti: 123e6
sama dengan 123
dengan 6 angka nol 123000000
.
Angka negatif setelah "e"
menyebabkan angka tersebut habis dibagi 1 dengan diberi angka nol. Misalnya 123e-6
berarti 0.000123
( 123
juta).
Untuk sistem angka yang berbeda:
Dapat menulis bilangan secara langsung dalam sistem hex ( 0x
), oktal ( 0o
) dan biner ( 0b
).
parseInt(str, base)
mem-parsing string str
menjadi bilangan bulat dalam sistem angka dengan base
yang diberikan, 2 ≤ base ≤ 36
.
num.toString(base)
mengonversi angka menjadi string dalam sistem angka dengan base
yang diberikan.
Untuk tes angka reguler:
isNaN(value)
mengonversi argumennya menjadi angka dan kemudian mengujinya sebagai NaN
Number.isNaN(value)
memeriksa apakah argumennya termasuk dalam tipe number
, dan jika ya, uji apakah argumennya termasuk NaN
isFinite(value)
mengonversi argumennya menjadi angka dan kemudian mengujinya karena bukan NaN/Infinity/-Infinity
Number.isFinite(value)
memeriksa apakah argumennya termasuk dalam tipe number
, dan jika ya, uji apakah argumen tersebut bukan NaN/Infinity/-Infinity
Untuk mengonversi nilai seperti 12pt
dan 100px
menjadi angka:
Gunakan parseInt/parseFloat
untuk konversi "lunak", yang membaca angka dari string dan kemudian mengembalikan nilai yang dapat dibaca sebelum kesalahan.
Untuk pecahan:
Pembulatan menggunakan Math.floor
, Math.ceil
, Math.trunc
, Math.round
atau num.toFixed(precision)
.
Pastikan untuk mengingat bahwa ada hilangnya presisi saat mengerjakan pecahan.
Fungsi matematika lainnya:
Lihat objek Math saat Anda membutuhkannya. Perpustakaannya sangat kecil tetapi dapat memenuhi kebutuhan dasar.
pentingnya: 5
Buat skrip yang meminta pengunjung memasukkan dua angka dan kemudian menampilkan jumlahnya.
Jalankan demonya
PS Ada gotcha dengan tipe.
let a = +prompt("Bilangan pertama?", ""); misalkan b = +prompt("Bilangan kedua?", ""); peringatan( a + b );
Perhatikan unary plus +
sebelum prompt
. Ini segera mengubah nilainya menjadi angka.
Jika tidak, a
dan b
akan menjadi string, jumlahnya akan menjadi gabungannya, yaitu: "1" + "2" = "12"
.
pentingnya: 4
Menurut dokumentasi Math.round
dan toFixed
kedua putaran ke angka terdekat: 0..4
mengarah ke bawah sedangkan 5..9
mengarah ke atas.
Misalnya:
peringatan( 1.35.toFixed(1) ); // 1.4
Pada contoh serupa di bawah, mengapa 6.35
dibulatkan menjadi 6.3
, bukan 6.4
?
peringatan( 6.35.toFixed(1) ); // 6.3
Bagaimana cara membulatkan 6.35
dengan benar?
Secara internal pecahan desimal 6.35
adalah biner tak berujung. Seperti biasa dalam kasus seperti ini, disimpan dengan kehilangan presisi.
Mari kita lihat:
peringatan( 6.35.toFixed(20) ); // 6.34999999999999964473
Hilangnya presisi dapat menyebabkan peningkatan dan penurunan angka. Dalam kasus khusus ini, angkanya menjadi sedikit lebih kecil, oleh karena itu dibulatkan ke bawah.
Dan untuk apa 1.35
?
peringatan( 1.35.toFixed(20) ); // 1.35000000000000008882
Di sini hilangnya presisi membuat angkanya sedikit lebih besar, sehingga dibulatkan.
Bagaimana kita bisa memperbaiki masalah dengan 6.35
jika kita ingin masalah tersebut dibulatkan dengan benar?
Kita harus mendekatkannya ke bilangan bulat sebelum membulatkan:
peringatan( (6,35 * 10).toFixed(20) ); // 63.50000000000000000000
Perhatikan bahwa 63.5
tidak memiliki kehilangan presisi sama sekali. Itu karena bagian desimal 0.5
sebenarnya adalah 1/2
. Pecahan yang dibagi pangkat 2
direpresentasikan secara persis dalam sistem biner, sekarang kita dapat membulatkannya:
alert( Math.round(6.35 * 10) / 10 ); // 6.35 -> 63.5 -> 64(dibulatkan) -> 6.4
pentingnya: 5
Buat fungsi readNumber
yang meminta nomor hingga pengunjung memasukkan nilai numerik yang valid.
Nilai yang dihasilkan harus dikembalikan sebagai angka.
Pengunjung juga dapat menghentikan proses dengan memasukkan baris kosong atau menekan “CANCEL”. Dalam hal ini, fungsi tersebut harus mengembalikan null
.
Jalankan demonya
Buka kotak pasir dengan tes.
fungsi nomor baca() { biarkan nomor; Mengerjakan { num = prompt("Masukkan nomornya?", 0); } sementara ( !isFinite(angka) ); if (angka === null || angka === '') mengembalikan nol; kembali + nomor; } alert(`Baca: ${readNumber()}`);
Solusinya sedikit lebih rumit karena kita perlu menangani baris null
/kosong.
Jadi kami benar-benar menerima masukan tersebut sampai menjadi “bilangan biasa”. Baik null
(cancel) maupun baris kosong juga sesuai dengan kondisi tersebut, karena dalam bentuk numeriknya adalah 0
.
Setelah kita berhenti, kita perlu memperlakukan null
dan baris kosong secara khusus (return null
), karena mengubahnya menjadi angka akan menghasilkan 0
.
Buka solusi dengan pengujian di kotak pasir.
pentingnya: 4
Lingkaran ini tidak terbatas. Itu tidak pernah berakhir. Mengapa?
misalkan saya = 0; sementara (saya != 10) { saya += 0,2; }
Itu karena i
tidak akan pernah sama dengan 10
.
Jalankan untuk melihat nilai sebenarnya dari i
:
misalkan saya = 0; sementara (saya < 11) { saya += 0,2; jika (i > 9.8 && i < 10.2) peringatan( i ); }
Tak satu pun dari mereka yang tepat 10
.
Hal seperti itu terjadi karena hilangnya presisi saat menjumlahkan pecahan seperti 0.2
.
Kesimpulan: hindari pemeriksaan kesetaraan saat bekerja dengan pecahan desimal.
pentingnya: 2
Fungsi bawaan Math.random()
menciptakan nilai acak dari 0
hingga 1
(tidak termasuk 1
).
Tuliskan fungsi random(min, max)
untuk menghasilkan bilangan floating-point acak dari min
hingga max
(tidak termasuk max
).
Contoh kerjanya:
peringatan( acak(1, 5) ); // 1.2345623452 peringatan( acak(1, 5) ); // 3.7894332423 peringatan( acak(1, 5) ); // 4.3435234525
Kita perlu “memetakan” semua nilai dari interval 0…1 menjadi nilai dari min
hingga max
.
Itu bisa dilakukan dalam dua tahap:
Jika kita mengalikan bilangan acak dari 0…1 dengan max-min
, maka interval nilai yang mungkin bertambah 0..1
menjadi 0..max-min
.
Sekarang jika kita menambahkan min
, interval yang mungkin menjadi dari min
hingga max
.
Fungsi:
fungsi acak(min, maks) { return min + Math.random() * (maks - min); } peringatan( acak(1, 5) ); peringatan( acak(1, 5) ); peringatan( acak(1, 5) );
pentingnya: 2
Buat fungsi randomInteger(min, max)
yang menghasilkan bilangan bulat acak dari min
hingga max
termasuk min
dan max
sebagai nilai yang mungkin.
Angka berapa pun dari interval min..max
harus muncul dengan probabilitas yang sama.
Contoh kerjanya:
peringatan(randomInteger(1, 5) ); // 1 peringatan(randomInteger(1, 5) ); // 3 peringatan(randomInteger(1, 5) ); // 5
Anda dapat menggunakan solusi dari tugas sebelumnya sebagai dasar.
Solusi paling sederhana namun salah adalah dengan menghasilkan nilai dari min
ke max
dan membulatkannya:
fungsi randomInteger(min, maks) { biarkan rand = min + Math.random() * (maks - min); return Math.round(rand); } peringatan(randomInteger(1, 3) );
Fungsinya berfungsi, tetapi salah. Kemungkinan mendapatkan nilai tepi min
dan max
adalah dua kali lebih kecil dibandingkan nilai tepi lainnya.
Jika Anda menjalankan contoh di atas berkali-kali, Anda akan dengan mudah melihat bahwa 2
yang paling sering muncul.
Hal ini terjadi karena Math.round()
mendapatkan angka acak dari interval 1..3
dan membulatkannya sebagai berikut:
nilai dari 1...hingga 1,4999999999 menjadi 1 nilai dari 1,5...hingga 2,4999999999 menjadi 2 nilai dari 2,5...hingga 2,9999999999 menjadi 3
Sekarang kita dapat melihat dengan jelas bahwa 1
mendapat nilai dua kali lebih kecil dari 2
. Dan hal yang sama dengan 3
.
Ada banyak solusi yang tepat untuk tugas ini. Salah satunya adalah dengan mengatur batas interval. Untuk memastikan interval yang sama, kita dapat menghasilkan nilai dari 0.5 to 3.5
, sehingga menambahkan probabilitas yang diperlukan pada tepinya:
fungsi randomInteger(min, maks) { // sekarang rand dari (min-0,5) hingga (maks+0,5) misalkan rand = min - 0,5 + Math.random() * (maks - min + 1); return Math.round(rand); } peringatan(randomInteger(1, 3) );
Cara alternatifnya adalah dengan menggunakan Math.floor
untuk angka acak dari min
hingga max+1
:
fungsi randomInteger(min, maks) { // di sini rand dari min hingga (maks+1) misalkan rand = min + Math.random() * (maks + 1 - min); return Math.floor(rand); } peringatan(randomInteger(1, 3) );
Sekarang semua interval dipetakan dengan cara ini:
nilai dari 1...hingga 1,9999999999 menjadi 1 nilai dari 2...hingga 2.9999999999 menjadi 2 nilai dari 3...menjadi 3.9999999999 menjadi 3
Semua interval memiliki panjang yang sama, sehingga distribusi akhir menjadi seragam.