Moderní JavaScript obsahuje dva druhy čísel:
-
Běžná čísla v JavaScriptu jsou uložena v 64-bitovém formátu IEEE-754. Jsou známa také jako „čísla s pohyblivou řádovou čárkou s dvojnásobnou přesností“. To jsou čísla, která používáme ve většině případů a v této kapitole o nich budeme hovořit.
-
Čísla typu BigInt představují celá čísla libovolné délky. Jsou někdy zapotřebí, neboť běžné celé číslo nemůže bezpečně překročit
(253-1)nebo být menší než-(253-1), jak jsme uvedli již dříve v kapitole Datové typy. Jelikož biginty se používají jen v některých speciálních oblastech, věnujeme jim zvláštní kapitolu Typ BigInt.
Zde tedy budeme hovořit o běžných číslech. Rozšiřme si své znalosti o nich.
Další způsoby, jak napsat číslo
Představme si, že musíme napsat 1 miliardu. Obvyklý způsob je:
let miliarda = 1000000000;
Můžeme jako oddělovač použít podtržítko _:
let miliarda = 1_000_000_000;
Podtržítko _ zde hraje roli „syntaktického cukru“, který činí číslo čitelnějším. Motor JavaScriptu podtržítka _ mezi číslicemi jednoduše ignoruje, takže je to přesně stejná miliarda jako výše uvedená.
V reálném životě se však snažíme vyhnout zápisu dlouhých sekvencí nul. Jsme na to příliš líní. Snažíme se zapsat miliardu nějak jako "1 mld." nebo 7 miliard 300 miliónů jako "7,3 mld.". To platí pro většinu velkých čísel.
V JavaScriptu můžeme číslo zkrátit tím, že za ně přidáme písmeno "e" a uvedeme počet nul:
let miliarda = 1e9; // 1 miliarda, doslova: 1 a 9 nul
alert( 7.3e9 ); // 7.3 miliard (totéž jako 7300000000 nebo 7_300_000_000)
Jinými slovy, e toto číslo násobí číslem 1 se zadaným počtem nul.
1e3 === 1 * 1000 // e3 znamená *1000
1.23e6 === 1.23 * 1000000 // e6 znamená *1000000
Nyní zapišme nějaké velmi malé číslo. Třeba 1 mikrosekundu (jednu milióntinu sekundy):
let mcs = 0.000001;
Stejně jako předtím nám může pomoci použití "e". Jestliže se chceme vyhnout explicitnímu zápisu nul, můžeme zapsat totéž jako:
let mcs = 1e-6; // pět nul nalevo od 1
Spočítáme-li nuly v čísle 0.000001, bude jich 6. Je to tedy přirozeně 1e-6.
Jinými slovy, záporné číslo za "e" znamená dělení číslem 1 se zadaným počtem nul:
// -3 znamená dělení číslem 1 se 3 nulami
1e-3 === 1 / 1000; // 0.001
// -6 znamená dělení číslem 1 se 6 nulami
1.23e-6 === 1.23 / 1000000; // 0.00000123
// příklad s větším číslem
1234e-2 === 1234 / 100; // 12.34, desetinná čárka se posune 2krát
Hexadecimální, binární a oktální čísla
Hexadecimální čísla (v šestnáctkové soustavě) se v JavaScriptu široce používají k reprezentaci barev, kódování znaků a mnoha dalším věcem. Pochopitelně tedy existuje kratší způsob, jak je zapsat: 0x a pak číslo.
Například:
alert( 0xff ); // 255
alert( 0xFF ); // 255 (totéž, malá a velká písmena se nerozlišují)
Binární (dvojková) a oktální (osmičková) soustava se používají jen vzácně, ale jsou také podporovány, a to za použití prefixů 0b a 0o:
let a = 0b11111111; // binární podoba 255
let b = 0o377; // oktální podoba 255
alert( a == b ); // true, stejné číslo 255 na obou stranách
Takovou podporu mají pouze tři číselné soustavy. Pro jiné číselné soustavy bychom měli použít funkci parseInt (kterou uvidíme později v této kapitole).
toString(základ)
Metoda číslo.toString(základ) vrátí řetězcovou reprezentaci čísla číslo v číselné soustavě o zadaném základu základ.
Příklad:
let číslo = 255;
alert( číslo.toString(16) ); // ff
alert( číslo.toString(2) ); // 11111111
Hodnota základ může být od 2 do 36. Standardně je to 10.
Běžná použití jsou:
-
základ=16 se používá pro hexadecimální barvy, kódování znaků atd., číslice mohou být
0..9neboA..F. -
základ=2 slouží zejména pro ladění bitových operací, číslice mohou být
0nebo1. -
základ=36 je maximum, číslice mohou být
0..9neboA..Z. K reprezentaci čísla se používá celá latinská abeceda. Legrační, ale užitečné využití36představuje případ, kdy potřebujeme změnit dlouhý číselný identifikátor na něco kratšího, například abychom vytvořili kratší URL. Můžeme jej snadno reprezentovat v číselné soustavě o základu36:alert( 123456..toString(36) ); // 2n9c
Prosíme všimněte si, že dvě tečky v 123456..toString(36) není překlep. Chceme-li volat metodu přímo na čísle, např. toString v uvedeném příkladu, pak za číslo musíme umístit dvě tečky ...
Kdybychom umístili jedinou tečku: 123456.toString(36), nastala by chyba, protože syntaxe JavaScriptu očekává za první tečkou desetinnou část. Když však uvedeme další tečku, JavaScript pozná, že desetinná část je prázdná, a nyní použije metodu.
Zaokrouhlování
Jedna z nejčastěji používaných operací při práci s čísly je zaokrouhlování.
Pro zaokrouhlování existuje několik vestavěných funkcí:
Math.floor- Zaokrouhluje dolů:
3.1se zaokrouhlí na3,-1.1se zaokrouhlí na-2. Math.ceil- Zaokrouhluje nahoru:
3.1se zaokrouhlí na4,-1.1se zaokrouhlí na-1. Math.round- Zaokrouhluje na nejbližší celé číslo:
3.1se zaokrouhlí na3,3.6se zaokrouhlí na4. V prostředních případech se3.5zaokrouhlí nahoru na4a-3.5se zaokrouhlí nahoru na-3. Math.trunc(není podporována v Internet Exploreru)- Odstraní vše za desetinnou čárkou bez zaokrouhlení:
3.1se převede na3,-1.1se převede na-1.
Rozdíly mezi těmito funkcemi shrnuje následující tabulka:
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 |
Tyto funkce pokrývají všechny možné způsoby zacházení s desetinnou částí čísla. Ale co když chceme zaokrouhlit číslo na n-tou číslici za desetinnou čárkou?
Máme například 1.2345 a chceme toto číslo zaokrouhlit na 2 desetinná místa, abychom dostali jen 1.23.
Existují dva způsoby, jak to udělat:
-
Násobit a dělit.
Abychom například zaokrouhlili číslo na 2. číslici za desetinnou čárkou, můžeme toto číslo vynásobit
100, zavolat zaokrouhlovací funkci a pak je znovu vydělit.let číslo = 1.23456; alert( Math.round(číslo * 100) / 100 ); // 1.23456 -> 123.456 -> 123 -> 1.23 -
Metoda toFixed(n) zaokrouhluje číslo na
nčíslic za desetinnou čárkou a vrací řetězcovou reprezentaci výsledku.let číslo = 12.34; alert( číslo.toFixed(1) ); // "12.3"Zaokrouhlí číslo nahoru nebo dolů na nejbližší hodnotu, podobně jako
Math.round:let číslo = 12.36; alert( číslo.toFixed(1) ); // "12.4"Všimněte si, že výsledkem
toFixedje řetězec. Je-li desetinná část čísla kratší, než bylo vyžadováno, na konec se přidají nuly:let číslo = 12.34; alert( číslo.toFixed(5) ); // "12.34000", přidají se nuly, aby číslic bylo přesně 5Můžeme jej převést na číslo pomocí unárního plus nebo volání
Number(), např. napsat+číslo.toFixed(5).
Nepřesné výpočty
Číslo je vnitřně reprezentováno v 64-bitovém formátu IEEE-754, takže se ukládá přesně do 64 bitů: 52 z nich se používá k uložení číslic, v 11 z nich je uložena pozice desetinné čárky a 1 bit je pro znaménko.
Je-li číslo opravdu velké, může toto 64-bitové úložiště překročit a stát se speciální číselnou hodnotou Infinity (nekonečno):
alert( 1e500 ); // Infinity
Co může být trochu méně zřejmé, ale stává se poměrně často, je ztráta přesnosti.
Uvažujme tento (nepravdivý!) test rovnosti:
alert( 0.1 + 0.2 == 0.3 ); // false
Je to tak. Jestliže ověříme, zda součet 0.1 a 0.2 je 0.3, dostaneme false.
Zvláštní! Co tedy je, když ne 0.3?
alert( 0.1 + 0.2 ); // 0.30000000000000004
Ouvej! Představme si, že vytváříme elektronický obchod a návštěvník si do košíku uloží zboží za $0.10 a za $0.20. Celková cena pak bude $0.30000000000000004. To každého překvapí.
Ale proč se to děje?
Číslo je v paměti uloženo ve své binární podobě, jako posloupnost bitů – jedniček a nul. Ale desetinná čísla jako 0.1 nebo 0.2, která v desítkové soustavě vypadají jednoduše, jsou ve své binární podobě ve skutečnosti nekonečná.
alert(0.1.toString(2)); // 0.0001100110011001100110011001100110011001100110011001101
alert(0.2.toString(2)); // 0.001100110011001100110011001100110011001100110011001101
alert((0.1 + 0.2).toString(2)); // 0.0100110011001100110011001100110011001100110011001101
Co je vlastně 0.1? Je to jedna děleno deseti 1/10, jedna desetina. V desítkové soustavě lze taková čísla snadno reprezentovat. Srovnejme si to s jednou třetinou: 1/3. Z ní se stane nekonečné desetinné číslo 0.33333(3).
Je tedy zaručeno, že dělení mocninami 10 bude v desítkové soustavě fungovat dobře, ale dělení třemi ne. Ze stejného důvodu je v binární soustavě zaručeno, že bude fungovat dělení mocninami 2, ale z 1/10 se stane nekonečné binární číslo.
V binární soustavě prostě neexistuje způsob, jak uložit přesně 0,1 nebo přesně 0,2, stejně jako v desítkové soustavě není způsob, jak uložit jako desetinné číslo jednu třetinu.
Číselný formát IEEE-754 to řeší zaokrouhlením na nejbližší možné číslo. Tato zaokrouhlovací pravidla nám běžně neumožňují vidět tuto „drobnou ztrátu přesnosti“, ale ta tam je.
Můžeme to vidět v akci:
alert( 0.1.toFixed(20) ); // 0.10000000000000000555
A když sečteme dvě čísla, jejich „ztráty přesnosti“ se sečtou.
Proto 0.1 + 0.2 není přesně 0.3.
Stejný problém existuje v mnoha jiných programovacích jazycích.
Přesně stejný výsledek vydají i PHP, Java, C, Perl nebo Ruby, protože jsou založeny na stejném číselném formátu.
Můžeme se tomuto problému vyhnout? Jistě. Nejspolehlivější metoda je zaokrouhlit výsledek pomocí metody toFixed(n):
let součet = 0.1 + 0.2;
alert( součet.toFixed(2) ); // "0.30"
Prosíme všimněte si, že toFixed vrací vždy řetězec. Zajišťuje, že za desetinnou tečkou má vždy 2 číslice. To se obzvláště hodí, když máme elektronický obchod a potřebujeme zobrazit $0.30. V jiných případech můžeme použít unární plus, abychom řetězec převedli na číslo:
let součet = 0.1 + 0.2;
alert( +součet.toFixed(2) ); // 0.3
Můžeme také dočasně násobit tato čísla 100 (nebo vyšším číslem), abychom z nich vytvořili celá čísla, provést výpočty a pak je znovu vydělit. Pak, jelikož provádíme výpočty s celými čísly, chyba se trochu sníží, ale při dělení ji stále dostaneme:
alert( (0.1 * 10 + 0.2 * 10) / 10 ); // 0.3
alert( (0.28 * 100 + 0.14 * 100) / 100); // 0.4200000000000001
Použití násobení a dělení tedy chybu zredukuje, ale úplně ji neodstraní.
Někdy se snažíme úplně se desetinným číslům vyhnout. Například když vytváříme obchod, můžeme ukládat ceny v centech namísto v dolarech. Ale co když aplikujeme slevu 30%? V praxi je úplné vyhnutí se desetinným číslům možné jen zřídka. Prostě je zaokrouhlujte, abyste odřízli „zbytky“, když je třeba.
Zkuste si spustit tohle:
// Ahoj! Já jsem samozvyšující se číslo!
alert( 9999999999999999 ); // zobrazí 10000000000000000
To trpí stejným neduhem: ztrátou přesnosti. Pro číslo je 64 bitů, 52 z nich lze použít k uložení číslic, ale to nestačí. Nejméně významné číslice tedy zmizí.
JavaScript při takových událostech nevyvolá chybu. Udělá, co může, aby se číslo vešlo do požadovaného formátu, ale naneštěstí tento formát není dostatečně velký.
Dalším legračním důsledkem této interní reprezentace čísel je existence dvou nul: 0 a -0.
Je to proto, že znaménko je reprezentováno jediným bitem, který může být nastaven na 1 nebo 0 pro jakékoli číslo včetně nuly.
Ve většině případů je tento rozdíl neznatelný, protože operátory jsou vytvořeny tak, aby s oběma nulami zacházely stejně.
Testy: isFinite a isNaN
Vzpomínáte si na tyto dvě speciální číselné hodnoty?
Infinity(a-Infinity) je speciální číselná hodnota, která je větší (menší) než cokoli jiného.NaNpředstavuje chybu.
Patří k typu number, ale nejsou to „normální“ čísla, takže existují speciální funkce, které je prověří:
-
isNaN(hodnota)převede svůj argument na číslo a pak jej otestuje, zda jeNaN:alert( isNaN(NaN) ); // true alert( isNaN("řetězec") ); // trueAle potřebujeme vůbec tuto funkci? Nemůžeme jednoduše použít porovnání
=== NaN? Bohužel ne. HodnotaNaNje unikátem, který se nerovná ničemu jinému, dokonce ani sám sobě:alert( NaN === NaN ); // false -
isFinite(hodnota)převede svůj argument na číslo a vrátítrue, jestliže je to skutečné číslo a neNaN/Infinity/-Infinity:alert( isFinite("15") ); // true alert( isFinite("řetězec") ); // false, protože je to speciální hodnota: NaN alert( isFinite(Infinity) ); // false, protože je to speciální hodnota: Infinity
Někdy se isFinite používá k ověření, zda řetězcová hodnota je skutečné číslo:
let číslo = +prompt("Zadejte číslo", '');
// bude true, pokud nezadáte Infinity, -Infinity nebo něco jiného než číslo
alert( isFinite(číslo) );
Prosíme všimněte si, že s prázdným řetězcem nebo s řetězcem složeným pouze z mezer se zachází jako s 0 ve všech číselných funkcích včetně isFinite.
Number.isNaN a Number.isFiniteMetody Number.isNaN a Number.isFinite jsou „striktnější“ verze funkcí isNaN a isFinite. Svůj argument nepřevádějí automaticky na číslo, ale místo toho ověří, zda jejich argument je typu number.
-
Number.isNaN(hodnota)vracítrue, jestliže argument je typunumbera jeNaN. V jakémkoli jiném případě vracífalse.alert( Number.isNaN(NaN) ); // true alert( Number.isNaN("řetězec" / 2) ); // true // Všimněte si rozdílu: alert( Number.isNaN("řetězec") ); // false, protože "řetězec" je typu string, ne typu number alert( isNaN("řetězec") ); // true, protože isNaN převede "řetězec" na číslo a jako výsledek této konverze získá NaN -
Number.isFinite(value)vracítrue, jestliže argument je typunumbera neníNaN/Infinity/-Infinity. V jakémkoli jiném případě vracífalse.alert( Number.isFinite(123) ); // true alert( Number.isFinite(Infinity) ); // false alert( Number.isFinite(2 / 0) ); // false // Všimněte si rozdílu: alert( Number.isFinite("123") ); // false, protože "123" je typu string, ne typu number alert( isFinite("123") ); // true, protože isFinite převede řetězec "123" na číslo 123
Svým způsobem jsou Number.isNaN a Number.isFinite jednodušší a přímější než funkce isNaN a isFinite. V praxi se však většinou používají isNaN a isFinite, jelikož jsou kratší na napsání.
Object.isExistuje speciální vestavěná metoda Object.is, která porovnává hodnoty stejně jako ===, ale ve dvou krajních případech je spolehlivější:
- Funguje pro
NaN:Object.is(NaN, NaN) === true, což je dobrá věc. - Hodnoty
0a-0jsou rozdílné:Object.is(0, -0) === false, technicky je to správně, protože vnitřně číslo obsahuje znaménkový bit, který se může lišit, i když jsou všechny ostatní bity nulové.
Ve všech ostatních případech je Object.is(a, b) totéž jako a === b.
Metodu Object.is zde zmiňujeme proto, že se často používá ve specifikaci JavaScriptu. Když interní algoritmus potřebuje porovnat, zda jsou dvě hodnoty přesně stejné, používá Object.is (interně nazvanou SameValue).
parseInt a parseFloat
Číselná konverze prováděná pomocí plus + nebo Number() je striktní. Není-li hodnota přesně číslo, konverze selže:
alert( +"100px" ); // NaN
Jedinou výjimkou jsou mezery na začátku nebo na konci řetězce, které jsou ignorovány.
V reálném životě však často máme hodnoty s jednotkami, např. "100px" nebo "12pt" v CSS. Navíc v mnoha zemích se symbol měny píše až za částku, takže máme "19€" a rádi bychom z toho získali číselnou hodnotu.
K tomu slouží funkce parseInt a parseFloat.
„Načítají“ číslo z řetězce tak dlouho, dokud to jde. Jakmile nastane chyba, vrátí nahromaděné číslo. Funkce parseInt vrátí celé číslo, zatímco parseFloat vrátí číslo s pohyblivou řádovou čárkou:
alert( parseInt('100px') ); // 100
alert( parseFloat('12.5em') ); // 12.5
alert( parseInt('12.3') ); // 12, vrátí se jen celá část
alert( parseFloat('12.3.4') ); // 12.3, druhá tečka ukončí načítání
Existují situace, v nichž parseInt/parseFloat vrátí NaN. To se stane tehdy, když nelze načíst ani jednu číslici:
alert( parseInt('a123') ); // NaN, první znak tento proces zastaví
parseInt(řetězec, soustava)Funkce parseInt() má nepovinný druhý parametr. Ten specifikuje základ číselné soustavy, takže parseInt může také načítat řetězce s hexadecimálními čísly, binárními čísly a podobně:
alert( parseInt('0xff', 16) ); // 255
alert( parseInt('ff', 16) ); // 255, funguje to i bez 0x
alert( parseInt('2n9c', 36) ); // 123456
Další matematické funkce
JavaScript má vestavěný objekt Math, který obsahuje malou knihovnu matematických funkcí a konstant.
Několik příkladů:
Math.random()-
Vrátí náhodné číslo od 0 do 1 (kromě 1).
alert( Math.random() ); // 0.1234567894322 alert( Math.random() ); // 0.5435252343232 alert( Math.random() ); // ... (jakákoli náhodná čísla) Math.max(a, b, c...)aMath.min(a, b, c...)-
Vrátí největší a nejmenší z libovolného počtu argumentů.
alert( Math.max(3, 5, -10, 0, 1) ); // 5 alert( Math.min(1, 2) ); // 1 Math.pow(n, exponent)-
Vrátí
numocněné na zadaný exponent.alert( Math.pow(2, 10) ); // 2 na 10 = 1024
V objektu Math jsou i další konstanty a funkce včetně goniometrických. Můžete je najít v dokumentaci k objektu Math.
Shrnutí
Abychom napsali číslo s mnoha nulami:
- Připojíme za číslo
"e"a počet nul. Například123e6je totéž jako123s 6 nulami:123000000. - Záporné číslo za
"e"způsobí, že číslo bude děleno číslem 1 se zadaným počtem nul. Například123e-6znamená0.000123(123milióntin).
Pro různé číselné soustavy:
- Můžeme zapisovat čísla přímo v hexadecimální (
0x), oktální (0o) a binární (0b) soustavě. parseInt(řetězec, základ)převedeřetězecna celé číslo v číselné soustavě o zadaném základuzáklad,2 ≤ základ ≤ 36.číslo.toString(základ)převede číslo na řetězec v číselné soustavě o zadaném základuzáklad.
Pro testování čísel:
isNaN(hodnota)převede svůj argument na číslo a pak testuje, zda jeNaNNumber.isNaN(hodnota)ověří, zda je její argument typunumber, a pokud ano, testuje, zda jeNaNisFinite(hodnota)převede svůj argument na číslo a pak testuje, zda neníNaN/Infinity/-InfinityNumber.isFinite(hodnota)ověří, zda je její argument typunumber, a pokud ano, testuje, zda neníNaN/Infinity/-Infinity
Pro převod hodnot jako 12pt nebo 100px na číslo:
- Pro „měkkou“ konverzi používejte
parseInt/parseFloat, která načte číslo z řetězce a pak vrátí hodnotu, kterou dokázala přečíst, než nastala chyba.
Pro desetinná čísla:
- Zaokrouhlujte pomocí
Math.floor,Math.ceil,Math.trunc,Math.roundnebočíslo.toFixed(přesnost). - Při práci s desetinnými čísly se ujistěte, že nezapomínáte na ztrátu přesnosti.
Další matematické funkce:
- Až je budete potřebovat, podívejte se na objekt Math. Je to velmi malá knihovna, ale základní potřeby dokáže pokrýt.
Komentáře
<code>, pro několik řádků je obalte značkou<pre>, pro více než 10 řádků vložte odkaz na pískoviště (plnkr, jsbin, codepen…)