V moderním JavaScriptu existují dva typy čísel:
-
Běžná čísla v JavaScriptu jsou uložena v 64bitovém formátu IEEE-754, známém také jako „čísla s plovoucí desetinnou čárkou s dvojitou přesností“. Toto jsou čísla, která používáme většinu času, a budeme o nich mluvit v této kapitole.
-
BigInt čísla představují celá čísla libovolné délky. Někdy jsou potřeba, protože běžné celé číslo nemůže bezpečně překročit
(253-1)
nebo být menší než-(253-1)
, jak jsme zmínili dříve v kapitole Datové typy. Protože biginty se používají v několika speciálních oblastech, věnujeme jim zvláštní kapitolu BigInt.
Takže zde budeme mluvit o běžných číslech. Rozšiřme své znalosti o nich.
Více způsobů, jak napsat číslo
Představte si, že potřebujeme napsat 1 miliardu. Zřejmý způsob je:
let billion = 1000000000;
Můžeme také použít podtržítko _
jako oddělovač:
let billion = 1_000_000_000;
Zde podtržítko _
hraje roli „syntaktického cukru“, činí číslo čitelnějším. JavaScript engine jednoduše ignoruje _
mezi číslicemi, takže je to přesně stejná miliarda jako výše.
Ve skutečném životě se však snažíme vyhnout psaní dlouhých sekvencí nul. Na to jsme příliš líní. Pokusíme se napsat něco jako "1bn"
za miliardu nebo "7.3bn"
za 7 miliard 300 milionů. Totéž platí pro většinu velkých čísel.
V JavaScriptu můžeme číslo zkrátit přidáním písmene "e"
k němu a zadáním počtu nul:
let billion = 1e9; // 1 billion, literally: 1 and 9 zeroes
alert( 7.3e9 ); // 7.3 billions (same as 7300000000 or 7_300_000_000)
Jinými slovy e
vynásobí číslo 1
s daným počtem nul.
1e3 === 1 * 1000; // e3 means *1000
1.23e6 === 1.23 * 1000000; // e6 means *1000000
Nyní napíšeme něco velmi malého. Řekněme 1 mikrosekundu (jedna miliontina sekundy):
let mсs = 0.000001;
Stejně jako předtím, pomocí "e"
může pomoct. Pokud bychom se chtěli vyhnout psaní nul explicitně, mohli bychom napsat totéž jako:
let mcs = 1e-6; // five zeroes to the left from 1
Pokud počítáme nuly v 0.000001
, je jich 6. Takže přirozeně je to 1e-6
.
Jinými slovy, záporné číslo za "e"
znamená dělení 1 s daným počtem nul:
// -3 divides by 1 with 3 zeroes
1e-3 === 1 / 1000; // 0.001
// -6 divides by 1 with 6 zeroes
1.23e-6 === 1.23 / 1000000; // 0.00000123
// an example with a bigger number
1234e-2 === 1234 / 100; // 12.34, decimal point moves 2 times
Hexadecimální, binární a osmičková čísla
Hexadecimální čísla se v JavaScriptu široce používají k reprezentaci barev, kódování znaků a pro mnoho dalších věcí. Přirozeně tedy existuje kratší způsob, jak je zapsat:0x
a potom číslo.
Například:
alert( 0xff ); // 255
alert( 0xFF ); // 255 (the same, case doesn't matter)
Binární a osmičkové číselné soustavy se používají zřídka, ale jsou také podporovány pomocí 0b
a 0o
předpony:
let a = 0b11111111; // binary form of 255
let b = 0o377; // octal form of 255
alert( a == b ); // true, the same number 255 at both sides
Existují pouze 3 číselné soustavy s takovou podporou. Pro ostatní číselné soustavy bychom měli použít funkci parseInt
(což uvidíme později v této kapitole).
toString(základ)
Metoda num.toString(base)
vrátí řetězcovou reprezentaci num
v číselné soustavě s daným base
.
Například:
let num = 255;
alert( num.toString(16) ); // ff
alert( num.toString(2) ); // 11111111
base
se může lišit od 2
na 36
. Ve výchozím nastavení je to 10
.
Běžné případy použití jsou:
-
základ=16 používá se pro hexadecimální barvy, kódování znaků atd., číslice mohou být
0..9
neboA..F
. -
základ=2 je většinou pro ladění bitových operací, číslice mohou být
0
nebo1
. -
základ=36 je maximum, číslic může být
0..9
neboA..Z
. K vyjádření čísla se používá celá latinská abeceda. Vtipný, ale užitečný případ pro36
je, když potřebujeme změnit dlouhý číselný identifikátor na něco kratšího, například vytvořit krátkou adresu URL. Lze jej jednoduše vyjádřit v číselné soustavě se základem36
:alert( 123456..toString(36) ); // 2n9c
Vezměte prosím na vědomí, že dvě tečky v 123456..toString(36)
není překlep. Pokud chceme volat metodu přímo na číslo, například toString
ve výše uvedeném příkladu pak musíme umístit dvě tečky ..
po něm.
Pokud bychom umístili jednu tečku:123456.toString(36)
, pak by došlo k chybě, protože syntaxe JavaScriptu implikuje desetinnou část za první tečkou. A pokud umístíme ještě jednu tečku, pak JavaScript ví, že desetinná část je prázdná a nyní jde o metodu.
Také mohl psát (123456).toString(36)
.
Zaokrouhlení
Jednou z nejpoužívanějších operací při práci s čísly je zaokrouhlování.
Existuje několik vestavěných funkcí pro zaokrouhlování:
Math.floor
- Zaokrouhluje dolů:
3.1
se změní na3
a-1.1
se změní na-2
. Math.ceil
- Zaokrouhluje nahoru:
3.1
se změní na4
a-1.1
se změní na-1
. Math.round
- Zaokrouhlí na nejbližší celé číslo:
3.1
se změní na3
,3.6
se změní na4
, střední velikost písmen:3.5
zaokrouhluje nahoru na4
taky. Math.trunc
(nepodporuje Internet Explorer)- Odebere cokoli za desetinnou čárkou bez zaokrouhlení:
3.1
se změní na3
,-1.1
se změní na-1
.
Zde je tabulka shrnující rozdíly mezi nimi:
Math.floor | Math.ceil | Math.round | Math.trunc | |
---|---|---|---|---|
3.1 | 3 | 4 | 3 | 3 |
3.6 | 3 | 4 | 4 | 3 |
-1.1 | -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ž bychom chtěli číslo zaokrouhlit na n-th
číslice za desetinnou čárkou?
Například máme 1.2345
a chcete jej zaokrouhlit na 2 číslice, dostanete pouze 1.23
.
Existují dva způsoby, jak to udělat:
-
Vynásobte a rozdělte.
Chcete-li například zaokrouhlit číslo na 2. číslici za desetinnou čárkou, můžeme číslo vynásobit
100
, zavolejte funkci zaokrouhlení a poté ji vydělte.let num = 1.23456; alert( Math.round(num * 100) / 100 ); // 1.23456 -> 123.456 -> 123 -> 1.23
-
Metoda toFixed(n) zaokrouhlí číslo na
n
číslic za tečkou a vrátí řetězcovou reprezentaci výsledku.let num = 12.34; alert( num.toFixed(1) ); // "12.3"
Tím se zaokrouhlí nahoru nebo dolů na nejbližší hodnotu, podobně jako
Math.round
:let num = 12.36; alert( num.toFixed(1) ); // "12.4"
Upozorňujeme, že výsledek
toFixed
je řetězec. Pokud je desetinná část kratší, než je požadováno, jsou na konec připojeny nuly:let num = 12.34; alert( num.toFixed(5) ); // "12.34000", added zeroes to make exactly 5 digits
Můžeme jej převést na číslo pomocí unárního plus nebo
Number()
zavolejte, např. napište+num.toFixed(5)
.
Nepřesné výpočty
Interně je číslo reprezentováno v 64bitovém formátu IEEE-754, takže je k uložení čísla přesně 64 bitů:52 z nich se používá k uložení číslic, 11 z nich k uložení pozice desetinné čárky a 1 bit je pro znamení.
Pokud je číslo opravdu velké, může přetéct 64bitové úložiště a stát se speciální číselnou hodnotou Infinity
:
alert( 1e500 ); // Infinity
Co může být trochu méně zřejmé, ale stává se to poměrně často, je ztráta přesnosti.
Zvažte tento (falešný!) test rovnosti:
alert( 0.1 + 0.2 == 0.3 ); // false
To je pravda, pokud zkontrolujeme, zda je součet 0.1
a 0.2
je 0.3
, dostaneme false
.
Zvláštní! Co to tedy je, když ne 0.3
?
alert( 0.1 + 0.2 ); // 0.30000000000000004
Au! Představte si, že vytváříte e-shop a návštěvník zadá $0.10
a $0.20
zboží do jejich košíku. Celková objednávka bude $0.30000000000000004
. To by každého překvapilo.
Ale proč se to děje?
Číslo je uloženo v paměti ve své binární podobě, posloupnosti bitů – jedniček a nul. Ale zlomky jako 0.1
, 0.2
které v desítkové soustavě vypadají jednoduše, jsou ve skutečnosti nekonečné zlomky ve své binární podobě.
Co je 0.1
? Je to jedna dělená deseti 1/10
, jedna desetina. V desítkové číselné soustavě jsou taková čísla snadno reprezentovatelná. Porovnejte to s jednou třetinou:1/3
. Stává se z něj nekonečný zlomek 0.33333(3)
.
Takže dělení mocninami 10
zaručeně funguje dobře v desítkové soustavě, ale dělení 3
není. Ze stejného důvodu je v binární číselné soustavě dělení mocninou 2
zaručeně funguje, ale 1/10
se stává nekonečným binárním zlomkem.
Neexistuje žádný způsob, jak uložit přesně 0,1 nebo přesně 0,2 pomocí dvojkové soustavy, stejně jako neexistuje způsob, jak uložit jednu třetinu jako desetinný zlomek.
Číselný formát IEEE-754 to řeší zaokrouhlením na nejbližší možné číslo. Tato pravidla zaokrouhlování nám normálně neumožňují vidět „malou ztrátu přesnosti“, ale existuje.
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.
PHP, Java, C, Perl, Ruby dávají přesně stejný výsledek, protože jsou založeny na stejném číselném formátu.
Můžeme problém obejít? Jistě, nejspolehlivější metodou je zaokrouhlit výsledek pomocí metody toFixed(n):
let sum = 0.1 + 0.2;
alert( sum.toFixed(2) ); // "0.30"
Vezměte prosím na vědomí, že toFixed
vždy vrátí řetězec. Zajišťuje, že má za desetinnou čárkou 2 číslice. To je vlastně výhodné, pokud máme e-shop a potřebujeme zobrazit $0.30
. V ostatních případech můžeme použít jednočlenné plus k vynucení čísla:
let sum = 0.1 + 0.2;
alert( +sum.toFixed(2) ); // 0.3
Můžeme také dočasně vynásobit čísla 100 (nebo větším číslem), abychom je proměnili na celá čísla, spočítali a pak vydělili zpět. Když pak počítáme s celými čísly, chyba se poněkud zmenšuje, ale stále ji dostáváme na dělení:
alert( (0.1 * 10 + 0.2 * 10) / 10 ); // 0.3
alert( (0.28 * 100 + 0.14 * 100) / 100); // 0.4200000000000001
Přístup násobení/rozdělení tedy snižuje chybu, ale neodstraňuje ji úplně.
Někdy jsme se mohli pokusit zlomkům vůbec vyhnout. Jako když jednáme s obchodem, pak můžeme uložit ceny v centech místo v dolarech. Co když ale uplatníme slevu 30 %? V praxi je úplné vyloučení frakcí jen zřídka možné. Jednoduše je zakulatíte, abyste v případě potřeby odřízli „ocásky“.
Vtipná věcZkuste spustit toto:
// Hello! I'm a self-increasing number!
alert( 9999999999999999 ); // shows 10000000000000000
Toto trpí stejným problémem:ztrátou přesnosti. Číslo má 64 bitů, 52 z nich lze použít k uložení číslic, ale to nestačí. Takže nejméně významné číslice zmizí.
JavaScript v takových událostech nespouští chybu. Snaží se, aby se číslo vešlo do požadovaného formátu, ale tento formát bohužel není dostatečně velký.
Dvě nuly
Dalším vtipným důsledkem vnitřní reprezentace čísel je existence dvou nul:0
a -0
.
Je to proto, že znaménko je reprezentováno jedním bitem, takže jej lze nebo nelze nastavit pro jakékoli číslo včetně nuly.
Ve většině případů je rozdíl nepostřehnutelný, protože operátory jsou vhodné k tomu, aby s nimi zacházely stejně.
Testy:isFinite a isNaN
Pamatujete si tyto dvě speciální číselné hodnoty?
Infinity
(a-Infinity
) je speciální číselná hodnota, která je větší (menší) než cokoliv jiného.NaN
představuje chybu.
Patří do typu number
, ale nejsou to „normální“ čísla, takže pro jejich kontrolu existují speciální funkce:
-
isNaN(value)
převede svůj argument na číslo a poté jej otestuje naNaN
:alert( isNaN(NaN) ); // true alert( isNaN("str") ); // true
Ale potřebujeme tuto funkci? Nemůžeme prostě použít srovnání
=== NaN
? Bohužel ne. HodnotaNaN
je unikátní v tom, že se nerovná ničemu, včetně sebe sama:alert( NaN === NaN ); // false
-
isFinite(value)
převede svůj argument na číslo a vrátítrue
pokud je to běžné číslo, nikoliNaN/Infinity/-Infinity
:alert( isFinite("15") ); // true alert( isFinite("str") ); // false, because a special value: NaN alert( isFinite(Infinity) ); // false, because a special value: Infinity
Někdy isFinite
se používá k ověření, zda je hodnota řetězce běžné číslo:
let num = +prompt("Enter a number", '');
// will be true unless you enter Infinity, -Infinity or not a number
alert( isFinite(num) );
Vezměte prosím na vědomí, že prázdný řetězec nebo řetězec obsahující pouze mezery je považován za 0
ve všech numerických funkcích včetně isFinite
.
Number.isNaN
a Number.isFinite
Metody Number.isNaN a Number.isFinite jsou „přísnější“ verze isNaN
a isFinite
funkcí. Nepřevádějí automaticky svůj argument na číslo, ale zkontrolují, zda patří do number
zadejte místo toho.
-
Number.isNaN(value)
vrátítrue
pokud argument patří donumber
zadejte a je toNaN
. V každém jiném případě vrátífalse
.alert( Number.isNaN(NaN) ); // true alert( Number.isNaN("str" / 2) ); // true // Note the difference: alert( Number.isNaN("str") ); // false, because "str" belongs to the string type, not the number type alert( isNaN("str") ); // true, because isNaN converts string "str" into a number and gets NaN as a result of this conversion
-
Number.isFinite(value)
vrátítrue
pokud argument patří donumber
typ a není toNaN/Infinity/-Infinity
. V každém jiném případě vrátífalse
.alert( Number.isFinite(123) ); // true alert( Number.isFinite(Infinity) ); //false alert( Number.isFinite(2 / 0) ); // false // Note the difference: alert( Number.isFinite("123") ); // false, because "123" belongs to the string type, not the number type alert( isFinite("123") ); // true, because isFinite converts string "123" into a number 123
Svým způsobem Number.isNaN
a Number.isFinite
jsou jednodušší a přímočařejší než isNaN
a isFinite
funkcí. V praxi však isNaN
a isFinite
se většinou používají, protože jsou kratší na psaní.
Object.is
Existuje speciální vestavěná metoda Object.is
který porovnává hodnoty jako ===
, ale je spolehlivější pro dva okrajové případy:
- Funguje s
NaN
:Object.is(NaN, NaN) === true
, to je dobrá věc. - Hodnoty
0
a-0
jsou různé:Object.is(0, -0) === false
, technicky je to pravda, protože interně má číslo znaménkový bit, který se může lišit, i když všechny ostatní bity jsou nuly.
Ve všech ostatních případech Object.is(a, b)
je stejný jako a === b
.
Zmiňujeme Object.is
zde, protože se často používá ve specifikaci JavaScriptu. Když interní algoritmus potřebuje porovnat dvě hodnoty, aby byly přesně stejné, použije Object.is
(interně nazývané SameValue).
parseInt a parseFloat
Číselný převod pomocí plus +
nebo Number()
je přísná. Pokud hodnota není přesně číslo, selže:
alert( +"100px" ); // NaN
Jedinou výjimkou jsou mezery na začátku nebo na konci řetězce, protože jsou ignorovány.
Ale v reálném životě máme často hodnoty v jednotkách, jako je "100px"
nebo "12pt"
v CSS. V mnoha zemích také následuje symbol měny za částkou, takže máme "19€"
a chtěli byste z toho extrahovat číselnou hodnotu.
To je to, co parseInt
a parseFloat
jsou pro.
„Přečtou“ číslo z řetězce, dokud to neumějí. V případě chyby je vráceno shromážděné číslo. Funkce parseInt
vrátí celé číslo, zatímco parseFloat
vrátí číslo s plovoucí desetinnou čárkou:
alert( parseInt('100px') ); // 100
alert( parseFloat('12.5em') ); // 12.5
alert( parseInt('12.3') ); // 12, only the integer part is returned
alert( parseFloat('12.3.4') ); // 12.3, the second point stops the reading
Existují situace, kdy parseInt/parseFloat
vrátí NaN
. Stává se to, když nelze přečíst žádné číslice:
alert( parseInt('a123') ); // NaN, the first symbol stops the process
Druhý argument z parseInt(str, radix)
parseInt()
funkce má volitelný druhý parametr. Specifikuje základ číselné soustavy, tedy parseInt
může také analyzovat řetězce hexadecimálních čísel, binárních čísel a tak dále:
alert( parseInt('0xff', 16) ); // 255
alert( parseInt('ff', 16) ); // 255, without 0x also works
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() ); // ... (any random numbers)
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, power)
-
Vrátí
n
zvýšen na daný výkon.alert( Math.pow(2, 10) ); // 2 in power 10 = 1024
V Math
je více funkcí a konstant objekt, včetně trigonometrie, kterou najdete v dokumentaci k objektu Math.
Shrnutí
Chcete-li psát čísla s mnoha nulami:
- Připojit
"e"
s nulami počítejte k číslu. Jako:123e6
je stejný jako123
se 6 nulami123000000
. - Záporné číslo za
"e"
způsobí, že se číslo vydělí 1 danými nulami. Např.123e-6
znamená0.000123
(123
miliontiny).
Pro různé číselné soustavy:
- Umí psát čísla přímo v hexadecimálním formátu (
0x
), osmičková (0o
) a binární (0b
) systémy. parseInt(str, base)
analyzuje řetězecstr
na celé číslo v číselné soustavě s danýmbase
,2 ≤ base ≤ 36
.num.toString(base)
převede číslo na řetězec v číselné soustavě s danýmbase
.
Pro běžné testy čísel:
isNaN(value)
převede svůj argument na číslo a poté jej otestuje naNaN
Number.isNaN(value)
zkontroluje, zda jeho argument patří donumber
type, a pokud ano, otestuje, zda jeNaN
isFinite(value)
převede svůj argument na číslo a poté jej otestuje, zda neníNaN/Infinity/-Infinity
Number.isFinite(value)
zkontroluje, zda jeho argument patří donumber
zadejte, a pokud ano, otestuje jej, zda neníNaN/Infinity/-Infinity
Pro převod hodnot jako 12pt
a 100px
na číslo:
- Použijte
parseInt/parseFloat
pro „měkký“ převod, který přečte číslo z řetězce a poté vrátí hodnotu, kterou mohli přečíst před chybou.
Pro zlomky:
- Zaokrouhlete pomocí
Math.floor
,Math.ceil
,Math.trunc
,Math.round
nebonum.toFixed(precision)
. - Nezapomeňte, že při práci se zlomky dochází ke ztrátě přesnosti.
Další matematické funkce:
- Zobrazte objekt Math, když je potřebujete. Knihovna je velmi malá, ale dokáže pokrýt základní potřeby.